diff --git a/.circleci/config.yml b/.circleci/config.yml index 5609def78ca..cd70928f15c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,12 +1,10 @@ version: 2.1 orbs: - aws-cli: circleci/aws-cli@3.1.4 - browser-tools: circleci/browser-tools@1.4.2 - macos: circleci/macos@2.3.6 + aws-cli: circleci/aws-cli@4.1.3 + browser-tools: circleci/browser-tools@1.4.8 win: circleci/windows@5.0.0 workflows: - version: 2 default: jobs: - prepare-linux: @@ -15,7 +13,7 @@ workflows: only: /.*/ - install-mbx-ci: requires: - - prepare-linux + - prepare-linux filters: tags: only: /.*/ @@ -50,12 +48,25 @@ workflows: filters: tags: only: /.*/ - - test-unit: + - setup-playwright: requires: - prepare-linux filters: tags: only: /.*/ + - test-unit: + requires: + - setup-playwright + filters: + tags: + only: /.*/ + - test-csp: + requires: + - setup-playwright + - build + filters: + tags: + only: /.*/ - test-query: requires: - prepare-linux @@ -68,9 +79,9 @@ workflows: filters: tags: only: /.*/ - - test-browser: + - test-typescript: requires: - - prepare-linux + - build filters: tags: only: /.*/ @@ -161,14 +172,14 @@ workflows: linux-defaults: &linux-defaults docker: - - image: cimg/node:18.16-browsers + - image: cimg/node:18.20-browsers working_directory: ~/mapbox-gl-js mac-defaults: &mac-defaults resource_class: macos.m1.medium.gen1 macos: - # https://circleci.com/docs/using-macos/#supported-xcode-versions - xcode: 14.3.0 # macOS 13.2 (Ventura) + # https://circleci.com/docs/using-macos/#supported-xcode-versions-silicon + xcode: 14.3.1 # macOS 13.2.1 (Ventura) environment: HOMEBREW_NO_AUTO_UPDATE: 1 working_directory: ~/mapbox-gl-js @@ -187,15 +198,15 @@ jobs: - checkout - restore_cache: keys: - - v0-linux-yarn-{{ .Branch }}-{{ checksum "yarn.lock" }} - - v0-linux-yarn-{{ .Branch }}- - - v0-linux-yarn- - - run: yarn --frozen-lockfile --cache-folder ~/.cache/yarn - - run: yarn run build-dev + - v0-linux-npm-{{ .Branch }}-{{ checksum "package-lock.json" }} + - v0-linux-npm-{{ .Branch }}- + - v0-linux-npm- + - run: npm ci --no-audit --no-fund + - run: npm run build-dev - save_cache: - key: v0-linux-yarn-{{ .Branch }}-{{ checksum "yarn.lock" }} + key: v0-linux-npm-{{ .Branch }}-{{ checksum "package-lock.json" }} paths: - - ~/.cache/yarn + - ~/.npm - persist_to_workspace: root: ~/ paths: @@ -228,8 +239,8 @@ jobs: keys: - v2-lint-{{ .Branch }} - v2-lint - - run: yarn run lint - - run: yarn run lint-css + - run: npm run lint + - run: npm run lint-css - save_cache: key: v2-lint-{{ .Branch }}-{{ .Revision }} paths: @@ -240,14 +251,20 @@ jobs: steps: - attach_workspace: at: ~/ - - run: yarn run build-prod-min - - run: yarn run build-prod - - run: yarn run build-csp - - run: yarn run build-css - - run: yarn run build-style-spec - - run: yarn run build-flow-types - - run: yarn run test-build + - run: npm run build-prod-min + - run: npm run build-prod + - run: npm run build-csp + - run: npm run build-css + - run: npm run build-style-spec + - run: npm run build-flow-types + - run: npm run test-build - run: while read l; do cp debug/$l test/release/$l; done < test/release/local_release_page_list.txt + - run: + name: Create mapbox-gl.tar.gz + command: | + TAR_PATH=mapbox-gl-${CIRCLE_TAG:-${CIRCLE_SHA1:0:6}}.tar.gz + tar -czvf $TAR_PATH test/release dist + mv $TAR_PATH test/release - store_artifacts: path: "dist" - store_artifacts: @@ -257,6 +274,17 @@ jobs: paths: - mapbox-gl-js/dist + setup-playwright: + <<: *linux-defaults + steps: + - attach_workspace: + at: ~/ + - run: npx playwright install chromium + - save_cache: + key: v0-playwright + paths: + - ~/.cache + check-bundle-size: <<: *linux-defaults steps: @@ -264,22 +292,25 @@ jobs: at: ~/ - run: name: Check bundle size - command: yarn check-bundle-size + command: npm run check-bundle-size test-flow: <<: *linux-defaults steps: - attach_workspace: at: ~/ - - run: yarn run test-flow + - run: npm run test-flow test-unit: <<: *linux-defaults steps: - attach_workspace: at: ~/ + - restore_cache: + keys: + - v0-playwright - run: - command: yarn run test-unit + command: npm run test-unit no_output_timeout: 5m test-query: @@ -288,12 +319,39 @@ jobs: - attach_workspace: at: ~/ - browser-tools/install-chrome - - run: yarn run test-query + - run: npm run test-query - store_test_results: path: test/integration/query-tests - store_artifacts: path: "test/integration/query-tests/index.html" + test-typescript: + <<: *linux-defaults + steps: + - attach_workspace: + at: ~/ + - run: + name: Build TypeScript + command: | + cd ./test/build/typescript && + npm ci && + npm run build && + rm -rf node_modules + - store_artifacts: + path: "test/build/typescript" + + test-csp: + <<: *linux-defaults + steps: + - attach_workspace: + at: ~/ + - restore_cache: + keys: + - v0-playwright + - run: + command: npm run test-csp + no_output_timeout: 5m + test-webpack: <<: *linux-defaults steps: @@ -303,7 +361,7 @@ jobs: name: Build Webpack command: | cd ./test/build/transpilation && - yarn && + npm ci && npm run build && rm -rf node_modules - store_artifacts: @@ -314,7 +372,7 @@ jobs: steps: - attach_workspace: at: ~/ - - run: yarn run test-style-spec + - run: npm run test-style-spec verify-codegen: <<: *linux-defaults @@ -324,36 +382,17 @@ jobs: - run: name: Verify codegen output command: | - yarn run codegen + npm run codegen git add -A && git diff --staged --exit-code | tee check.patch - - test-browser: - <<: *linux-defaults - steps: - - attach_workspace: - at: ~/ - - browser-tools/install-browser-tools: - chrome-version: "114.0.5735.90" - - run: yarn run build-token - - run: - name: Test Chrome - environment: - SELENIUM_BROWSER: chrome - TAP_COLORS: 1 - command: yarn run test-browser - - run: - name: Test Firefox - environment: - SELENIUM_BROWSER: firefox - TAP_COLORS: 1 - command: yarn run test-browser + - store_artifacts: + path: "check.patch" test-expressions: <<: *linux-defaults steps: - attach_workspace: at: ~/ - - run: yarn run test-expressions + - run: npm run test-expressions deploy-release: <<: *linux-defaults @@ -384,7 +423,7 @@ jobs: - run: name: Running tests in parallel command: | - yarn run test-render + npm run test-render - store_test_results: path: test/integration/render-tests - store_artifacts: @@ -396,7 +435,7 @@ jobs: - attach_workspace: at: ~/ - browser-tools/install-chrome - - run: yarn run test-render-prod + - run: npm run test-render-prod - store_test_results: path: test/integration/render-tests - store_artifacts: @@ -408,7 +447,7 @@ jobs: - attach_workspace: at: ~/ - browser-tools/install-chrome - - run: yarn run test-render-csp + - run: npm run test-render-csp - store_test_results: path: test/integration/render-tests - store_artifacts: @@ -420,7 +459,7 @@ jobs: - attach_workspace: at: ~/ - browser-tools/install-firefox - - run: yarn run test-render-firefox + - run: npm run test-render-firefox - store_test_results: path: test/integration/render-tests - store_artifacts: @@ -432,16 +471,16 @@ jobs: - checkout - restore_cache: keys: - - v0-mac-yarn-{{ .Branch }}-{{ checksum "yarn.lock" }} - - v0-mac-yarn-{{ .Branch }}- - - v0-mac-yarn- + - v0-mac-npm-{{ .Branch }}-{{ checksum "package-lock.json" }} + - v0-mac-npm-{{ .Branch }}- + - v0-mac-npm- - run: nvm install && cat .nvmrc | nvm alias default `xargs` - - run: yarn --frozen-lockfile --cache-folder ~/.cache/yarn + - run: npm ci --no-audit --no-fund - save_cache: - key: v0-mac-yarn-{{ .Branch }}-{{ checksum "yarn.lock" }} + key: v0-mac-npm-{{ .Branch }}-{{ checksum "package-lock.json" }} paths: - - ~/.cache/yarn - - run: yarn run build-dev + - ~/.npm + - run: npm run build-dev - persist_to_workspace: root: ~/ paths: @@ -458,7 +497,7 @@ jobs: name: Creating test list command: | circleci tests glob "test/integration/render-tests/**/*.json" | circleci tests split --split-by=timings > tests-to-run.txt - - run: yarn run test-render + - run: npm run test-render - store_test_results: path: test/integration/render-tests - store_artifacts: @@ -469,7 +508,7 @@ jobs: steps: - attach_workspace: at: ~/ - - run: yarn run test-render-safari + - run: npm run test-render-safari - store_test_results: path: test/integration/render-tests - store_artifacts: @@ -479,24 +518,31 @@ jobs: <<: *windows-defaults steps: - checkout + - restore_cache: + keys: + - v2-windows-npm-{{ .Branch }}-{{ checksum "package-lock.json" }} + - v2-windows-npm-{{ .Branch }}- + - v2-windows-npm- - run: - name: Setup Node.js and Yarn + name: Setup Node.js command: | $nodeVersion = Get-Content .nvmrc nvm install $nodeVersion nvm use $nodeVersion - npm install -g yarn - - run: yarn --frozen-lockfile - - run: yarn run build-dev + - run: npm ci --no-audit --no-fund --cache ~/.npm + - save_cache: + key: v2-windows-npm-{{ .Branch }}-{{ checksum "package-lock.json" }} + paths: + - ~/log.txt + - store_artifacts: + path: "test/integration/render-tests/index.html" + - run: npm run build-dev - run: name: Clean up workspace to persist faster command: | Remove-Item .git -Recurse -Force; Remove-Item node_modules/flow-bin -Recurse -Force; - Remove-Item node_modules/tap -Recurse -Force; - Remove-Item node_modules/gl -Recurse -Force; Remove-Item node_modules/@mapbox/mvt-fixtures/real-world/osm-qa-* -Recurse -Force; - Remove-Item node_modules/react-devtools-core -Recurse -Force; Remove-Item node_modules/@octokit -Recurse -Force; Remove-Item node_modules/puppeteer-core -Recurse -Force; Remove-Item node_modules/lodash -Recurse -Force; @@ -512,12 +558,11 @@ jobs: - attach_workspace: at: ~/ - run: - name: Setup Node.js and Yarn + name: Setup Node.js command: | $nodeVersion = Get-Content .nvmrc nvm install $nodeVersion nvm use $nodeVersion - npm install -g yarn # The browser-tools orb doesn't work on Windows, so we install chrome manually. - run: name: Installing Chrome on Windows @@ -536,7 +581,7 @@ jobs: name: Creating test list command: | circleci tests glob "test/integration/render-tests/**/*.json" | circleci tests split | Out-File -Encoding utf8 -FilePath tests-to-run.txt - - run: yarn run test-render + - run: npm run test-render - store_test_results: path: test/integration/render-tests - store_artifacts: @@ -550,4 +595,11 @@ jobs: name: Trigger SLA performance tests command: | sha=$(git rev-parse HEAD) - curl --location --request POST 'https://circleci.com/api/v2/project/github/mapbox/mapbox-gl-js-performance-internal/pipeline' --header 'Content-Type: application/json' -u $CIRCLECI_API_TOKEN: -d "{ \"parameters\": { \"setup_sha\": \"$sha\", \"setup_source_branch\": \"internal\" } }" + response_code=$(curl --location --write-out "%{http_code}" --output /dev/stderr --request POST 'https://circleci.com/api/v2/project/github/mapbox/mapbox-gl-js-performance-internal/pipeline' --header 'Content-Type: application/json' -u $CIRCLECI_API_TOKEN: -d "{ \"parameters\": { \"setup_sha\": \"$sha\", \"setup_source_branch\": \"internal\" } }") + + if [[ "$response_code" =~ ^2 ]]; then + echo "Success: HTTP 2xx response" + else + echo "Error: Non-2xx response code - $response_code" + exit 1 + fi diff --git a/.eslintrc b/.eslintrc index f12482aec99..d296dee8aaf 100644 --- a/.eslintrc +++ b/.eslintrc @@ -60,7 +60,7 @@ "no-eq-null": "off", "no-lonely-if": "off", "no-new": "off", - "no-unused-vars": ["error", {"argsIgnorePattern": "^_$"}], + "no-unused-vars": ["error", {"argsIgnorePattern": "^_"}], "no-warning-comments": "error", "no-mixed-operators": ["error", {"groups": [["&", "|", "^", "~", "<<", ">>", ">>>"], ["&&", "||"]]}], "object-curly-spacing": ["error", "never"], @@ -129,7 +129,8 @@ "jsdoc/require-property-description": "off", "jsdoc/require-property-name": "off", "jsdoc/require-property-type": "off", - "jsdoc/require-returns-type": "off", + "jsdoc/require-returns-type": "off" + } }, { @@ -143,7 +144,6 @@ "src/ui/control/**", "src/ui/handler/**", "src/geo/lng_lat.js", - "src/geo/lng_lat_bounds.js", "src/geo/mercator_coordinate.js", "src/source/**", ], @@ -161,7 +161,7 @@ "matchingFileName": "src/fake_filename_for_jsdoc_examples", "rejectExampleCodeRegex": " - \ No newline at end of file + diff --git a/debug/2762.html b/debug/2762.html index 753f0131a2f..112d4190ede 100644 --- a/debug/2762.html +++ b/debug/2762.html @@ -20,6 +20,7 @@ var map = new mapboxgl.Map({ container: 'map', + devtools: true, style: { "version": 8, "sources": { diff --git a/debug/3895.html b/debug/3895.html index 60190858145..570801eff71 100644 --- a/debug/3895.html +++ b/debug/3895.html @@ -20,6 +20,7 @@ var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, center: [-68.13734351262877, 45.137451890638886], zoom: 5, style: 'mapbox://styles/mapbox/streets-v10', diff --git a/debug/3d-playground.html b/debug/3d-playground.html index 6cb532b24e7..c4fd4e00968 100644 --- a/debug/3d-playground.html +++ b/debug/3d-playground.html @@ -95,7 +95,9 @@ this.floodLightColor = {r:255, g:255, b:146}; this.floodLightIntensity = 0.0; this.floodLightGroundAttenuation = 0.69; + this.floodLightGroundRadius = 0.0; this.buildingColor = {r:255, g:255, b:255}; + this.buildingAlpha = 1.0; this.buildingOpacity = 1; this.cutoffFadeRange = 0.0; this.ambientLightColor = {r:255, g:255, b:255}; @@ -108,11 +110,6 @@ this.directionalLightShadowIntensity = 1.0; this.edgeRadius = 0.3; this.roundedRoof = true; - this.fps = 0; - this.continuousRedraw = true; - - this.fpsTimer = 0; - this.frameCounter = 0; this.mapProjection = 'globe'; this.orthographicProjectionAtLowPitch = false; @@ -120,16 +117,6 @@ this.enableBuildings = true; this.debug = { - showTiles: false, - showCollisions: false, - showOverdraw: false, - wireframe: { - terrain: false, - layers2D: false, - layers3D: false, - }, - tileAABBDebug: false, - freezeTileCoverage: false }; // Terrain debug widget @@ -142,8 +129,6 @@ this.debug.unexaggerated = 0; this.terrainElevationDebugWidget.addMonitor(this.debug, 'exaggerated'); this.terrainElevationDebugWidget.addMonitor(this.debug, 'unexaggerated'); - - this.debug.numShadowCascadesRendered = 2; // Default value until view is moved }; const demo3d = new Demo3D(); @@ -236,63 +221,6 @@ map.setStyle('mapbox://styles/' + value); }); - // FPS - - // Update FPS every second - demo3d.fpsTimer = window.setInterval(() => { - demo3d.fps = map.painter.frameCounter - demo3d.frameCounter; - demo3d.frameCounter = map.painter.frameCounter; - }, 1000); - - map.repaint = demo3d.continuousRedraw; - const fps = gui.addFolder({title:'FPS', expanded: true}); - const continuousRedraw = fps.addInput(demo3d, 'continuousRedraw'); - continuousRedraw.on('change', (ev) => { - map.repaint = demo3d.continuousRedraw; - }); - - fps.addMonitor(demo3d, 'fps'); - fps.addMonitor(demo3d, 'fps', { - label:'graph', - view: 'graph', - min: 0, - max: 140, - }); - - { - const debug = gui.addFolder({title:'Debug', expanded: false}); - - debug.addInput(demo3d.debug, 'showTiles').on('change', (ev) => { - map.showTileBoundaries = demo3d.debug.showTiles; - }); - debug.addInput(demo3d.debug, 'showCollisions').on('change', (ev) => { - map.showCollisionBoxes = demo3d.debug.showCollisions; - }); - debug.addInput(demo3d.debug, 'showOverdraw').on('change', (ev) => { - map.showOverdrawInspector = demo3d.debug.showOverdraw; - }); - - const wireframePane = debug.addFolder({title:'Wireframe', expanded: true}); - wireframePane.addInput(demo3d.debug.wireframe, 'terrain'); - wireframePane.addInput(demo3d.debug.wireframe, 'layers2D'); - wireframePane.addInput(demo3d.debug.wireframe, 'layers3D'); - wireframePane.on('change', (ev) => { - map.showTerrainWireframe = demo3d.debug.wireframe.terrain; - map.showLayers2DWireframe = demo3d.debug.wireframe.layers2D; - map.showLayers3DWireframe = demo3d.debug.wireframe.layers3D; - }); - - debug.addInput(demo3d.debug, 'tileAABBDebug').on('change', (ev) => { - map.showTileAABBs = demo3d.debug.tileAABBDebug; - }); - debug.addInput(demo3d.debug, 'freezeTileCoverage').on('change', (ev) => { - map.transform.freezeTileCoverage = demo3d.debug.freezeTileCoverage; - map._update(); - }); - - debug.addMonitor(demo3d.debug, 'numShadowCascadesRendered'); - } - // Lights const lights = gui.addFolder({title:'Lighting', expanded: true}); const enable3dLights = lights.addInput(demo3d, 'enable3dLights'); @@ -385,6 +313,7 @@ }); const verticalGradient = buildings.addInput(demo3d, 'verticalGradient'); const buildingColor = buildings.addInput(demo3d, 'buildingColor'); + const buildingAlpha = buildings.addInput(demo3d, 'buildingAlpha', {min:0.0, max:1.0}); const buildingOpacity = buildings.addInput(demo3d, 'buildingOpacity', {min:0.0, max:1.0}); const cutoffFadeRange = buildings.addInput(demo3d, 'cutoffFadeRange', {min:0.0, max:1.0}); const edgeRadius = buildings.addInput(demo3d, 'edgeRadius'); @@ -393,7 +322,7 @@ buildings.on('change', (ev) => { map.setPaintProperty(demo3d.buildingExtrusion, 'fill-extrusion-vertical-gradient', demo3d.verticalGradient); map.setPaintProperty(demo3d.buildingExtrusion, 'fill-extrusion-color', - 'rgb(' + demo3d.buildingColor.r + ', ' + demo3d.buildingColor.g + ', ' + demo3d.buildingColor.b + ')'); + 'rgba(' + demo3d.buildingColor.r + ', ' + demo3d.buildingColor.g + ', ' + demo3d.buildingColor.b + ', ' + demo3d.buildingAlpha + ')'); map.setPaintProperty(demo3d.buildingExtrusion, 'fill-extrusion-opacity', demo3d.buildingOpacity); map.setPaintProperty(demo3d.buildingExtrusion, 'fill-extrusion-cutoff-fade-range', demo3d.cutoffFadeRange); map.setLayoutProperty(demo3d.buildingExtrusion, 'fill-extrusion-edge-radius', demo3d.edgeRadius); @@ -416,6 +345,7 @@ const floodLightColor = groundEffects.addInput(demo3d, 'floodLightColor'); const floodLightIntensity = groundEffects.addInput(demo3d, 'floodLightIntensity', {min:0.0, max:1.0}); const floodLightGroundAttenuation = groundEffects.addInput(demo3d, 'floodLightGroundAttenuation', {min:0.0, max:1.0}); + const floodLightGroundRadius = groundEffects.addInput(demo3d, 'floodLightGroundRadius', {min:-50.0, max:50.0}); groundEffects.on('change', (ev) => { map.setPaintProperty(demo3d.buildingExtrusion, 'fill-extrusion-ambient-occlusion-intensity', demo3d.aoIntensity); map.setPaintProperty(demo3d.buildingExtrusion, 'fill-extrusion-ambient-occlusion-radius', demo3d.aoWallRadius); @@ -426,6 +356,7 @@ 'rgb(' + demo3d.floodLightColor.r + ', ' + demo3d.floodLightColor.g + ', ' + demo3d.floodLightColor.b + ')'); map.setPaintProperty(demo3d.buildingExtrusion, 'fill-extrusion-flood-light-intensity', demo3d.floodLightIntensity); map.setPaintProperty(demo3d.buildingExtrusion, 'fill-extrusion-flood-light-ground-attenuation', demo3d.floodLightGroundAttenuation); + map.setPaintProperty(demo3d.buildingExtrusion, 'fill-extrusion-flood-light-ground-radius', demo3d.floodLightGroundRadius); }); }; @@ -437,7 +368,9 @@ bearing: 60, style: window.dynamic, hash: true, - projection: 'globe' + projection: 'globe', + devtools: true, + repaint: true }); const r = 255 * 0.75; @@ -448,7 +381,6 @@ let first = true; map.on('style.load', function() { - // map.showOverdrawInspector = true; updateLights(demo3d.enable3dLights, true); if (!map.getSource('mapbox-dem')) { map.addSource('mapbox-dem', { @@ -554,13 +486,6 @@ demo3d.terrainElevationDebugWidget.refresh(); }); - map.on('move', (e) => { - demo3d.debug.numShadowCascadesRendered = 0; - const shadowRenderer = map.painter.shadowRenderer; - if (shadowRenderer) { - demo3d.debug.numShadowCascadesRendered = shadowRenderer._numCascadesToRender; - } - }); const lerp = (a, b, t) => { if (Array.isArray(a) && Array.isArray(b)) { diff --git a/debug/7438.html b/debug/7438.html index f2dcbf41db3..3d47d58713e 100644 --- a/debug/7438.html +++ b/debug/7438.html @@ -20,6 +20,7 @@ var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, zoom: 12.5, maxZoom: 24, center: [-77.01866, 38.888], diff --git a/debug/7517.html b/debug/7517.html index dde2730b815..2216f8539fa 100644 --- a/debug/7517.html +++ b/debug/7517.html @@ -81,6 +81,7 @@ const map = new mapboxgl.Map({ container: 'map', + devtools: true, style: 'mapbox://styles/mapbox/streets-v9', center: [-68.13734351262877, 45.137451890638886], zoom: 5, @@ -136,4 +137,4 @@ - \ No newline at end of file + diff --git a/debug/access_token.js b/debug/access_token.js index 80bfcaedd53..13812ac2f76 100644 --- a/debug/access_token.js +++ b/debug/access_token.js @@ -3,17 +3,19 @@ mapboxgl.accessToken = getAccessToken(); function getAccessToken() { - var accessToken = ( - process.env.MapboxAccessToken || - process.env.MAPBOX_ACCESS_TOKEN || - getURLParameter('access_token') || - localStorage.getItem('accessToken') || + const accessToken = [ + typeof process !== 'undefined' && process.env.MapboxAccessToken, + typeof process !== 'undefined' && process.env.MAPBOX_ACCESS_TOKEN, + getURLParameter('access_token'), + localStorage.getItem('accessToken'), // this token is a fallback for CI and testing. it is domain restricted to localhost 'pk.eyJ1IjoibWFwYm94LWdsLWpzIiwiYSI6ImNram9ybGI1ajExYjQyeGxlemppb2pwYjIifQ.LGy5UGNIsXUZdYMvfYRiAQ' - ); + ].find(Boolean); try { localStorage.setItem('accessToken', accessToken); - } catch (_) {} + } catch (_) { + // no-op + } return accessToken; } diff --git a/debug/animate-point-along-route.html b/debug/animate-point-along-route.html index c4ccccba162..a5511ae5cae 100644 --- a/debug/animate-point-along-route.html +++ b/debug/animate-point-along-route.html @@ -16,7 +16,7 @@ top: 0; left: 0; } - + .overlay button { font: 600 12px/20px 'Helvetica Neue', Arial, Helvetica, sans-serif; background-color: #3386c0; @@ -28,7 +28,7 @@ cursor: pointer; border-radius: 3px; } - + .overlay button:hover { background-color: #4ea0da; } @@ -49,6 +49,7 @@ const map = new mapboxgl.Map({ container: 'map', + devtools: true, style: 'mapbox://styles/mapbox/streets-v11', center: [-96, 37.8], zoom: 2.5 diff --git a/debug/animate.html b/debug/animate.html index 5b9adfd066d..7d4025e18f3 100644 --- a/debug/animate.html +++ b/debug/animate.html @@ -19,6 +19,7 @@ var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, style: 'mapbox://styles/mapbox/streets-v11', center: [0, 0], zoom: 4 diff --git a/debug/antimeridian.html b/debug/antimeridian.html index c0f8bfb4338..74753e9d5d7 100644 --- a/debug/antimeridian.html +++ b/debug/antimeridian.html @@ -21,6 +21,7 @@ var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, style: 'mapbox://styles/mapbox/streets-v11', zoom: 6, center: [179.012, -18.124], diff --git a/debug/atmosphere.html b/debug/atmosphere.html index 135e1453ac5..1bd3a504cfc 100644 --- a/debug/atmosphere.html +++ b/debug/atmosphere.html @@ -11,21 +11,25 @@ .mapboxgl-canvas { background-image: url("https://img.favpng.com/9/21/21/royalty-free-photography-illustration-png-favpng-d38x16Mw2kxSFqtUSXG8gT5CQ_t.jpg"); } - #flyTo { + #flyTo { + padding: 10px 20px; + margin-top: 22px; + } + + #container { position: absolute; - top: 0; left: 0; - margin: 15px; - font-size: 14px; - padding: 8px; - background-color: lightblue; - font-weight: bold; + top: 1em; + left: 1em; }
- +
+
+ +
@@ -52,6 +56,7 @@ var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, zoom: 2, center: [78.7193, 36.2927], style: 'mapbox://styles/mapbox/streets-v11', @@ -162,7 +167,8 @@ updatePreset('preset1'); window.onload = function() { - var gui = new dat.GUI(); // eslint-disable-line + var gui = new dat.GUI({autoPlace: false}); // eslint-disable-line + document.getElementById('gui').appendChild(gui.domElement); var presets = gui.add(guiParams, 'presets', [ 'preset1', diff --git a/debug/auth.html b/debug/auth.html index 4fa5be668c4..f64a9a5602f 100644 --- a/debug/auth.html +++ b/debug/auth.html @@ -34,6 +34,7 @@ var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, zoom: 12.5, center: [-122.4194, 37.7749], style, diff --git a/debug/bounds.html b/debug/bounds.html index 3cf8059ebc9..2892f659032 100644 --- a/debug/bounds.html +++ b/debug/bounds.html @@ -24,6 +24,7 @@ @@ -34,13 +31,14 @@ const r = 255 * 0.65; var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, zoom: 3, center: [-77.01866, 38.888], style: 'mapbox://styles/mapbox/streets-v9', - hash: true + hash: true, + repaint: true }); map._showQueryGeometry = true; -map.repaint = true; map.addControl(new mapboxgl.NavigationControl()); map.addControl(new mapboxgl.GeolocateControl()); @@ -103,28 +101,6 @@ map.setProjection(this.checked ? "globe" : null); }; -document.getElementById('show-tile-boundaries-checkbox').onclick = function() { - map.showTileBoundaries = !!this.checked; -}; - -document.getElementById('show-symbol-collision-boxes-checkbox').onclick = function() { - map.showCollisionBoxes = !!this.checked; -}; - -document.getElementById('repaint-checkbox').onclick = function() { - map.repaint = !!this.checked; - if (this.checked) { - this['frameCounter'] = map.painter.frameCounter; - this['fpsTimer'] = window.setInterval(() => { - document.getElementById('fps').innerHTML = `${(map.painter.frameCounter - this.frameCounter) / 2}`; - this.frameCounter = map.painter.frameCounter; - }, 2000); - } else { - window.clearInterval(this.fpsTimer); - document.getElementById('fps').innerHTML = `0`; - } -}; - let hovered = []; window.addEventListener('mousemove', function(e) { e.point = new mapboxgl.Point(e.clientX, e.clientY); diff --git a/debug/cluster.html b/debug/cluster.html index 315eded5c9f..02a407e24e6 100644 --- a/debug/cluster.html +++ b/debug/cluster.html @@ -20,6 +20,7 @@ var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, zoom: 1, center: [0, 0], style: 'mapbox://styles/mapbox/cjf4m44iw0uza2spb3q0a7s41', diff --git a/debug/color_spaces.html b/debug/color_spaces.html index f3bc0164bb5..646ea7874f2 100644 --- a/debug/color_spaces.html +++ b/debug/color_spaces.html @@ -35,6 +35,7 @@ var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, zoom: 2.5, center: [-77.01866, 38.888], style: 'mapbox://styles/mapbox/streets-v9', diff --git a/debug/config.html b/debug/config.html index d2e15908d2a..751c2d61925 100644 --- a/debug/config.html +++ b/debug/config.html @@ -20,6 +20,7 @@ var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, zoom: 12.5, center: [-122.4194, 37.7749], style: { diff --git a/debug/csp-static.html b/debug/csp-static.html index 62f15d1b6b3..e6f6deb47a6 100644 --- a/debug/csp-static.html +++ b/debug/csp-static.html @@ -25,6 +25,7 @@ var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, zoom: 12.5, center: [-77.01866, 38.888], style: 'mapbox://styles/mapbox/streets-v10', diff --git a/debug/csp.html b/debug/csp.html index 92a31563d8e..436aa7e054b 100644 --- a/debug/csp.html +++ b/debug/csp.html @@ -21,6 +21,7 @@ var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, zoom: 12.5, center: [-77.01866, 38.888], style: 'mapbox://styles/mapbox/streets-v10', diff --git a/debug/custom-layer-globe.html b/debug/custom-layer-globe.html index fb6d0641eee..8279a3ff026 100644 --- a/debug/custom-layer-globe.html +++ b/debug/custom-layer-globe.html @@ -22,7 +22,6 @@


-
@@ -36,6 +35,7 @@ diff --git a/debug/custom-source.html b/debug/custom-source.html index e5f8584ce6c..8d9c9962a7b 100644 --- a/debug/custom-source.html +++ b/debug/custom-source.html @@ -27,6 +27,7 @@ const map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, center: [0, 0], zoom: 0, style: 'mapbox://styles/mapbox/light-v10', diff --git a/debug/custom3d.html b/debug/custom3d.html index b96b6a05640..22f1f66263a 100644 --- a/debug/custom3d.html +++ b/debug/custom3d.html @@ -20,6 +20,7 @@ var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, zoom: 1.69, center: [-9.4, -26.8], bearing: 131, diff --git a/debug/dasharray.html b/debug/dasharray.html index be1b7d2590d..9779d604787 100644 --- a/debug/dasharray.html +++ b/debug/dasharray.html @@ -20,6 +20,7 @@ var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, zoom: 4, center: [0, 0], style: {sources: {}, version: 8, layers: []} diff --git a/debug/debug-empty.html b/debug/debug-empty.html index e92859c6cfa..28fb162466f 100644 --- a/debug/debug-empty.html +++ b/debug/debug-empty.html @@ -30,6 +30,7 @@ - \ No newline at end of file + diff --git a/debug/debug.html b/debug/debug.html index 0ecb337042d..c1a2479dbfc 100644 --- a/debug/debug.html +++ b/debug/debug.html @@ -36,6 +36,7 @@ var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, zoom: 12.5, center: [-77.01866, 38.888], style: 'mapbox://styles/mapbox/streets-v10', diff --git a/debug/default-image.html b/debug/default-image.html index ac4350d9e56..1fd7459f546 100644 --- a/debug/default-image.html +++ b/debug/default-image.html @@ -20,6 +20,7 @@ var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, zoom: 4, center: [-96, 37.8], style: 'mapbox://styles/mapbox/light-v10', diff --git a/debug/dynamic-filter.html b/debug/dynamic-filter.html index b864fc07c91..c6ee7da4c89 100644 --- a/debug/dynamic-filter.html +++ b/debug/dynamic-filter.html @@ -33,6 +33,7 @@ var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, zoom: 10.852, center: [-120.30344797631889, 38.11726797649675], style: 'mapbox://styles/mapbox/streets-v11', diff --git a/debug/dynamic.js b/debug/dynamic.js index c39e2c4c122..3ab382eb4eb 100644 --- a/debug/dynamic.js +++ b/debug/dynamic.js @@ -156,7 +156,7 @@ window.dynamic = { }, "3dbuildings": { "type": "batched-model", - "url": "mapbox://mapbox.mapbox-3dbuildings-v1-beta" + "url": "mapbox://mapbox.mbx-3dbuildings-v2-stg" }, "mapbox-dem": { "type": "raster-dem", @@ -7722,6 +7722,10 @@ window.dynamic = { ] }, "paint": { + "icon-color-saturation": ["config", "icon-color-saturation"], + "icon-color-contrast": ["config", "icon-color-contrast"], + "icon-color-brightness-min": ["config", "icon-color-brightness-min"], + "icon-color-brightness-max": ["config", "icon-color-brightness-max"], "icon-image-cross-fade": [ "interpolate", [ "linear" ], @@ -8971,6 +8975,36 @@ window.dynamic = { ] ] }, + "schema": { + "icon-color-saturation": { + "default": 0, + "type": "number", + "metadata": { + "mapbox:title": "Icon Saturation" + } + }, + "icon-color-contrast": { + "default": 0, + "type": "number", + "metadata": { + "mapbox:title": "Icon Contrast" + } + }, + "icon-color-brightness-min": { + "default": 0, + "type": "number", + "metadata": { + "mapbox:title": "Icon Minimum Brightness" + } + }, + "icon-color-brightness-max": { + "default": 1, + "type": "number", + "metadata": { + "mapbox:title": "Icon Maximum Brightness" + } + } + }, "lights": [ { "id": "ambient", diff --git a/debug/dynamic.json b/debug/dynamic.json index c8ec0219c53..edc831ec69d 100644 --- a/debug/dynamic.json +++ b/debug/dynamic.json @@ -155,7 +155,7 @@ }, "3dbuildings": { "type": "batched-model", - "url": "mapbox://mapbox.mapbox-3dbuildings-v1-beta" + "url": "mapbox://mapbox.mbx-3dbuildings-v2-stg" } }, "sprite": "mapbox://sprites/mapbox-map-design/clhrnvstg01yh01pn02ty6w7p/82qb27u5bmlfnboenyglbwsa3", diff --git a/debug/events.html b/debug/events.html index 7c92d9e17c9..bc225014afc 100644 --- a/debug/events.html +++ b/debug/events.html @@ -100,6 +100,7 @@ - + diff --git a/debug/fog-demo.html b/debug/fog-demo.html index d244a4c6fc4..61b1d352f56 100644 --- a/debug/fog-demo.html +++ b/debug/fog-demo.html @@ -19,6 +19,7 @@ var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, zoom: 4, center: [119.94552235709455, -11.48731083806907], pitch: 85, diff --git a/debug/fog.html b/debug/fog.html index 94850961ac9..fea0c331fd1 100644 --- a/debug/fog.html +++ b/debug/fog.html @@ -8,7 +8,11 @@ - -
-
-
+
+ +
+
+
+
+ - + diff --git a/debug/image-perspective.html b/debug/image-perspective.html index 84c93a2ba8c..3e00e152398 100644 --- a/debug/image-perspective.html +++ b/debug/image-perspective.html @@ -20,6 +20,7 @@ var map = new mapboxgl.Map({ container: 'map', + devtools: true, maxZoom: 21, minZoom: 1, zoom: 13.3, diff --git a/debug/image.html b/debug/image.html index 1b662f65be5..ca0d9e41d30 100644 --- a/debug/image.html +++ b/debug/image.html @@ -64,6 +64,7 @@ var map = new mapboxgl.Map({ container: 'map', + devtools: true, minZoom: 14, zoom: 17, center: [-122.514426, 37.562984], diff --git a/debug/imports.html b/debug/imports.html index 2139cad3b22..1a783250823 100644 --- a/debug/imports.html +++ b/debug/imports.html @@ -127,6 +127,7 @@ var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, center: [-122.40784, 37.78432], zoom: 15, pitch: 60, diff --git a/debug/index.html b/debug/index.html index 3672d434cfb..da89278107e 100644 --- a/debug/index.html +++ b/debug/index.html @@ -20,6 +20,7 @@ var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, zoom: 12.5, center: [-122.4194, 37.7749], hash: true, diff --git a/debug/is-safari.html b/debug/is-safari.html index 2da4af0395f..65c24126ae3 100644 --- a/debug/is-safari.html +++ b/debug/is-safari.html @@ -22,6 +22,7 @@ var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, zoom: 12.5, center: [-77.01866, 38.888], style: 'mapbox://styles/mapbox/streets-v10', diff --git a/debug/line-gradient.html b/debug/line-gradient.html index 89b443e3504..9d57ac9a8f9 100644 --- a/debug/line-gradient.html +++ b/debug/line-gradient.html @@ -19,6 +19,7 @@ + + + + + + diff --git a/debug/locale.html b/debug/locale.html index 248731dedd0..4eed3526661 100644 --- a/debug/locale.html +++ b/debug/locale.html @@ -20,6 +20,7 @@ var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, zoom: 12.5, center: [-77.01866, 38.888], style: 'mapbox://styles/mapbox/streets-v10', diff --git a/debug/markers-custom.html b/debug/markers-custom.html index 9aba62d821c..d8bb72760c9 100644 --- a/debug/markers-custom.html +++ b/debug/markers-custom.html @@ -45,6 +45,7 @@ var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, zoom: 3, style: 'mapbox://styles/mapbox/streets-v9', hash: true, diff --git a/debug/markers.html b/debug/markers.html index 083de411034..bc22e30cc49 100644 --- a/debug/markers.html +++ b/debug/markers.html @@ -37,6 +37,7 @@ var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, zoom: 12.5, center: [-77.01866, 38.888], style: 'mapbox://styles/mapbox/streets-v9', diff --git a/debug/measure-light.html b/debug/measure-light.html index 775e08d8c5f..5fea05687e2 100644 --- a/debug/measure-light.html +++ b/debug/measure-light.html @@ -9,11 +9,17 @@ -
+
+
@@ -62,7 +68,8 @@ } window.onload = function() { - var gui = new dat.GUI(); // eslint-disable-line + var gui = new dat.GUI({autoPlace: false}); // eslint-disable-line + document.getElementById('container').appendChild(gui.domElement); var style = gui.add(demo3d, 'style', [ 'mapbox/streets-v11', @@ -125,6 +132,7 @@ var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, zoom: 13.5, center: [-122.45814, 37.76159], style: 'mapbox://styles/' + demo3d.style, @@ -350,4 +358,4 @@ - \ No newline at end of file + diff --git a/debug/mobile_scroll.html b/debug/mobile_scroll.html index 949ae2dedd7..ee5065e2768 100644 --- a/debug/mobile_scroll.html +++ b/debug/mobile_scroll.html @@ -21,6 +21,7 @@ var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, zoom: 12.5, center: [-77.01866, 38.888], style: 'mapbox://styles/mapbox/streets-v10', diff --git a/debug/model.html b/debug/model.html index d89be252e3a..2b72d45467a 100644 --- a/debug/model.html +++ b/debug/model.html @@ -164,6 +164,7 @@ var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, style }); diff --git a/debug/multiple.html b/debug/multiple.html index 339693fafde..21d2f16ae10 100644 --- a/debug/multiple.html +++ b/debug/multiple.html @@ -66,7 +66,8 @@ center: [-122.514426, 37.562984], bearing: -96, style, - hash: false + hash: false, + devtools: true, }); map.addControl(new mapboxgl.GeolocateControl({ diff --git a/debug/no_wrap.html b/debug/no_wrap.html index fd23a4438f6..daf312d095d 100644 --- a/debug/no_wrap.html +++ b/debug/no_wrap.html @@ -20,6 +20,7 @@ var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, zoom: 12.5, center: [-77.01866, 38.888], style: 'mapbox://styles/mapbox/streets-v10', diff --git a/debug/padding.html b/debug/padding.html index dab8c287e8c..1625bb22c42 100644 --- a/debug/padding.html +++ b/debug/padding.html @@ -27,6 +27,7 @@ var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, zoom: 12.5, center: [-122.4194, 37.7749], style: 'mapbox://styles/mapbox/streets-v10', diff --git a/debug/pathological-flyto.html b/debug/pathological-flyto.html index bc1c5d3c7c0..1f731ee8ea7 100644 --- a/debug/pathological-flyto.html +++ b/debug/pathological-flyto.html @@ -115,6 +115,7 @@ var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, zoom: 12.5, center: [-122.4194, 37.7749], hash: false, @@ -198,4 +199,4 @@ - \ No newline at end of file + diff --git a/debug/popup.html b/debug/popup.html index 9cd27258731..5c51a2b1763 100644 --- a/debug/popup.html +++ b/debug/popup.html @@ -20,6 +20,7 @@ var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, zoom: 12.5, center: [-77.01866, 38.888], style: 'mapbox://styles/mapbox/streets-v10', diff --git a/debug/preload-tiles.html b/debug/preload-tiles.html index a69b2ebbf5d..016d13fc316 100644 --- a/debug/preload-tiles.html +++ b/debug/preload-tiles.html @@ -28,6 +28,7 @@ var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, zoom: 12.5, center: [-122.4194, 37.7749], style: 'mapbox://styles/mapbox/streets-v11', diff --git a/debug/projections.html b/debug/projections.html index 36a28d72efb..27ba5673402 100644 --- a/debug/projections.html +++ b/debug/projections.html @@ -140,6 +140,7 @@ const map = new mapboxgl.Map({ container: 'map', + devtools: true, hash: true, style: 'mapbox://styles/mapbox/streets-v12', zoom: 0, diff --git a/debug/query_features.html b/debug/query_features.html index 4b8294d9200..248b48027d8 100644 --- a/debug/query_features.html +++ b/debug/query_features.html @@ -25,6 +25,7 @@ var map = new mapboxgl.Map({ container: 'map', + devtools: true, zoom: 12.5, center: [-77.01866, 38.888], hash: true, diff --git a/debug/raster-array.html b/debug/raster-array.html new file mode 100644 index 00000000000..64b16a039d7 --- /dev/null +++ b/debug/raster-array.html @@ -0,0 +1,148 @@ + + + + + Mapbox GL JS debug page + + + + + + + +
+
+ + + + + + + diff --git a/debug/raster-color.html b/debug/raster-color.html index 943781373bc..9f1e9c8a8b6 100644 --- a/debug/raster-color.html +++ b/debug/raster-color.html @@ -9,6 +9,11 @@ body { margin: 0; padding: 0; } html, body { height: 100%; } #map { height: calc(100% - 40px); } + #container { + position: absolute; + top: 1em; + left: 1em; + } #controls { position: fixed; z-index: 1; @@ -33,6 +38,7 @@
+
@@ -63,6 +69,7 @@ function GUIParams () { this.preset = "Satellite"; this.opacity = 1; + this.elevation = 0; Object.assign(this, presets[this.preset]); } @@ -194,7 +201,8 @@ "source": "raster", "id": "raster", "paint": { - "raster-opacity": guiParams.opacity + "raster-opacity": guiParams.opacity, + "raster-elevation": guiParams.elevation } }] }); @@ -202,8 +210,10 @@ var map = window.map = new mapboxgl.Map({ container: "map", + devtools: true, style: {version: 8, layers: [], sources: {}}, hash: true, + projection: 'globe', transformRequest (url, type) { if (type !== "Tile") return; const preset = presets[guiParams.preset]; @@ -214,7 +224,8 @@ }); map.once("load", function () { - var gui = window.gui = new dat.GUI(); // eslint-disable-line + var gui = window.gui = new dat.GUI({autoPlace: false}); // eslint-disable-line + document.getElementById('container').appendChild(gui.domElement); const preset = gui.add(guiParams, "preset", Object.keys(presets)); const tileset = gui.add(guiParams, "tileset", Object.keys(tilesets)); @@ -222,6 +233,7 @@ const mix = gui.add(guiParams, "rasterColorMix", Object.keys(mixes)); const range = gui.add(guiParams, "rasterColorRange", Object.keys(ranges)); const opacity = gui.add(guiParams, "opacity", 0, 1); + const elevation = gui.add(guiParams, "elevation", 0, 10000000); function setColorScale(scale) { const range = ranges[guiParams.rasterColorRange]; @@ -272,7 +284,12 @@ document.getElementById("gradient").style.opacity = opac; }); + elevation.onChange(opac => { + map.setPaintProperty("raster", "raster-elevation", opac); + }); + setTileset(guiParams.tileset); + map.setFog({}); }); diff --git a/debug/raster-particle-layer.html b/debug/raster-particle-layer.html new file mode 100644 index 00000000000..cbe75204821 --- /dev/null +++ b/debug/raster-particle-layer.html @@ -0,0 +1,237 @@ + + + + + Raster particle layer + + + + + +
+
+ + + + + + + + diff --git a/debug/raster-streets.html b/debug/raster-streets.html index d069569bd50..2f3f0ad9e55 100644 --- a/debug/raster-streets.html +++ b/debug/raster-streets.html @@ -20,6 +20,7 @@ var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, zoom: 12.5, center: [-77.01866, 38.888], style: { diff --git a/debug/raster.html b/debug/raster.html new file mode 100644 index 00000000000..1df89aec3b3 --- /dev/null +++ b/debug/raster.html @@ -0,0 +1,134 @@ + + + + Mapbox GL JS debug page + + + + + + + +
+
+ + + + + + + + diff --git a/debug/render-test.html b/debug/render-test.html index c34797669e0..e3d6ef2a1ed 100644 --- a/debug/render-test.html +++ b/debug/render-test.html @@ -51,13 +51,16 @@ }); map.removeControl(map._logoControl); - if (debug) map.showTileBoundaries = true; map.repaint = true; await map.once('load'); await applyOperations(map, style.metadata.test); + + map.repaint = true; + await new Promise(resolve => requestAnimationFrame(map._requestDomTask.bind(map, resolve))); + diff --git a/debug/rtl.html b/debug/rtl.html index f39e4c61b58..a74e8008d76 100644 --- a/debug/rtl.html +++ b/debug/rtl.html @@ -35,6 +35,7 @@ var goodPluginURL = 'https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-rtl-text/v0.2.0/mapbox-gl-rtl-text.js'; var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, zoom: 16.5, center: [44.355435, 33.258814], style: 'mapbox://styles/mapbox/streets-v11', diff --git a/debug/satellite.html b/debug/satellite.html index 0f87778892e..e617f12f834 100644 --- a/debug/satellite.html +++ b/debug/satellite.html @@ -30,6 +30,7 @@ var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, zoom: 12.5, center: [-77.01866, 38.888], style: 'mapbox://styles/mapbox/satellite-v9', diff --git a/debug/scroll_zoom_blocker.html b/debug/scroll_zoom_blocker.html index 6393a9da218..78e1e96adf2 100644 --- a/debug/scroll_zoom_blocker.html +++ b/debug/scroll_zoom_blocker.html @@ -17,7 +17,7 @@ transform: scale(0.7); transform-origin: top left; background-color: black; - } + } #map { height: 100%; width: 100%; @@ -45,7 +45,7 @@ - +
@@ -80,6 +80,7 @@ } else { const map = new mapboxgl.Map({ container: 'map', + devtools: true, zoom: 12.5, center: [-77.01866, 38.888], style: 'mapbox://styles/mapbox/streets-v11', diff --git a/debug/setstyle.html b/debug/setstyle.html index 0098d754135..e250990060f 100644 --- a/debug/setstyle.html +++ b/debug/setstyle.html @@ -26,6 +26,7 @@ var style = dark; var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, zoom: 2, center: [-77.01866, 38.888], style, diff --git a/debug/shadows.html b/debug/shadows.html index 46bbf17f4fa..809bd4474f5 100644 --- a/debug/shadows.html +++ b/debug/shadows.html @@ -8,6 +8,11 @@
-
Loading...
diff --git a/debug/stretchable.html b/debug/stretchable.html index fb1cbf8d124..72aa14fb17a 100644 --- a/debug/stretchable.html +++ b/debug/stretchable.html @@ -20,6 +20,7 @@ var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, style: 'mapbox://styles/mapbox/streets-v10', hash: true }); diff --git a/debug/symbols-generated.html b/debug/symbols-generated.html index 0a7b0690084..80fceb40a46 100644 --- a/debug/symbols-generated.html +++ b/debug/symbols-generated.html @@ -20,6 +20,7 @@ const map = new mapboxgl.Map({ container: 'map', + devtools: true, style: 'mapbox://styles/mapbox/satellite-v9', center: [-79.197693, 43.832545], zoom: 15 diff --git a/debug/terrain-debug.html b/debug/terrain-debug.html index cd61857ec21..038f8c79ff5 100644 --- a/debug/terrain-debug.html +++ b/debug/terrain-debug.html @@ -21,21 +21,14 @@
-
-
-
-


-
-






-
@@ -44,6 +37,7 @@ var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, zoom: 13.5, center: [-122.45814, 37.76159], style: 'mapbox://styles/mapbox/streets-v11', @@ -276,35 +270,10 @@ document.getElementById('custom-checkbox').onclick(); }); -document.getElementById('show-tile-boundaries-checkbox').onclick = function() { - map.showTileBoundaries = !!this.checked; -}; - -document.getElementById('show-terrain-wireframe-checkbox').onclick = function() { - map.showTerrainWireframe = !!this.checked; -}; - -document.getElementById('show-symbol-collision-boxes-checkbox').onclick = function() { - map.showCollisionBoxes = !!this.checked; -}; - -document.getElementById('show-overdraw-checkbox').onclick = function() { - map.showOverdrawInspector = !!this.checked; -}; - -document.getElementById('show-aabb-checkbox').onclick = function() { - map.showTileAABBs = !!this.checked; -}; - document.getElementById('globe-checkbox').onclick = function() { map.setProjection(this.checked ? {name: "globe"} : null); }; -document.getElementById('freeze-tile-coverage-checkbox').onclick = function() { - map.transform.freezeTileCoverage = !!this.checked; - map._update(); -}; - document.getElementById('terrain-checkbox').onclick = function() { map.setTerrain(this.checked ? {"source": "mapbox-dem"} : null); }; @@ -345,20 +314,6 @@ .forEach(layer => { if (map.getLayer(layer)) map.setLayoutProperty(layer, 'visibility', this.checked ? 'visible' : 'none'); }); }; -document.getElementById('repaint-checkbox').onclick = function() { - map.repaint = !!this.checked; - if (this.checked) { - this['frameCounter'] = map.painter.frameCounter; - this['fpsTimer'] = window.setInterval(() => { - document.getElementById('fps').innerHTML = `${(map.painter.frameCounter - this.frameCounter) / 2}`; - this.frameCounter = map.painter.frameCounter; - }, 2000); - } else { - window.clearInterval(this.fpsTimer); - document.getElementById('fps').innerHTML = `0`; - } -}; - document.getElementById('circles-checkbox').onclick = function() { map.setLayoutProperty('population', 'visibility', this.checked ? 'visible' : 'none'); }; diff --git a/debug/terrain-hike.html b/debug/terrain-hike.html index 11fd2c5ac95..e4ae5497ffe 100644 --- a/debug/terrain-hike.html +++ b/debug/terrain-hike.html @@ -33,6 +33,7 @@ - \ No newline at end of file + diff --git a/debug/threejs.html b/debug/threejs.html index 10805ad3a38..84af9356d30 100644 --- a/debug/threejs.html +++ b/debug/threejs.html @@ -21,6 +21,7 @@ var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, antialias: true, zoom: 16.5, center: [-79.390307, 43.658956], diff --git a/debug/token.html b/debug/token.html index f9b982aa20b..959c0ca6a87 100644 --- a/debug/token.html +++ b/debug/token.html @@ -25,6 +25,7 @@ var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, zoom: 12.5, center: [-77.01866, 38.888], style: 'mapbox://styles/mapbox/streets-v10', diff --git a/debug/update_image.html b/debug/update_image.html index 33e66e79a7d..84a9851b877 100644 --- a/debug/update_image.html +++ b/debug/update_image.html @@ -20,6 +20,7 @@ const map = new mapboxgl.Map({ container: 'map', + devtools: true, minZoom: 1, zoom: 6, center: [-76, 43.5], diff --git a/debug/variable-anchor-with-icon-text-fit.html b/debug/variable-anchor-with-icon-text-fit.html index a8bf146dc84..a277830a3f6 100644 --- a/debug/variable-anchor-with-icon-text-fit.html +++ b/debug/variable-anchor-with-icon-text-fit.html @@ -124,9 +124,10 @@ var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, style }); - \ No newline at end of file + diff --git a/debug/video-export.html b/debug/video-export.html index 473e422fee7..ec733c7dc2b 100644 --- a/debug/video-export.html +++ b/debug/video-export.html @@ -22,6 +22,7 @@ const map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, center: [7.533634776071096, 45.486077107185565], zoom: 13.5, pitch: 61, diff --git a/debug/video.html b/debug/video.html index fc9405cd23a..1dcc1f6a3b9 100644 --- a/debug/video.html +++ b/debug/video.html @@ -81,6 +81,7 @@ }; var map = new mapboxgl.Map({ container: 'map', + devtools: true, minZoom: 14, zoom: 17, center: [-122.514426, 37.562984], diff --git a/debug/wind-512-mrt/2/3/1.mrt b/debug/wind-512-mrt/2/3/1.mrt new file mode 100644 index 00000000000..bb3a2130c67 Binary files /dev/null and b/debug/wind-512-mrt/2/3/1.mrt differ diff --git a/debug/wind-512-mrt/3/6/2.mrt b/debug/wind-512-mrt/3/6/2.mrt new file mode 100644 index 00000000000..ab4fa8bbb34 Binary files /dev/null and b/debug/wind-512-mrt/3/6/2.mrt differ diff --git a/debug/wind-512-mrt/3/6/3.mrt b/debug/wind-512-mrt/3/6/3.mrt new file mode 100644 index 00000000000..e7b056d3a38 Binary files /dev/null and b/debug/wind-512-mrt/3/6/3.mrt differ diff --git a/debug/wind-512-mrt/3/7/2.mrt b/debug/wind-512-mrt/3/7/2.mrt new file mode 100644 index 00000000000..8bb98ea44f7 Binary files /dev/null and b/debug/wind-512-mrt/3/7/2.mrt differ diff --git a/debug/wind-512-mrt/3/7/3.mrt b/debug/wind-512-mrt/3/7/3.mrt new file mode 100644 index 00000000000..05cf9994d1b Binary files /dev/null and b/debug/wind-512-mrt/3/7/3.mrt differ diff --git a/debug/wind-512-mrt/4/13/5.mrt b/debug/wind-512-mrt/4/13/5.mrt new file mode 100644 index 00000000000..7aac30e46ff Binary files /dev/null and b/debug/wind-512-mrt/4/13/5.mrt differ diff --git a/debug/wind-512-mrt/4/13/6.mrt b/debug/wind-512-mrt/4/13/6.mrt new file mode 100644 index 00000000000..0b8cd966487 Binary files /dev/null and b/debug/wind-512-mrt/4/13/6.mrt differ diff --git a/debug/wind-512-mrt/4/14/5.mrt b/debug/wind-512-mrt/4/14/5.mrt new file mode 100644 index 00000000000..45ded5650bc Binary files /dev/null and b/debug/wind-512-mrt/4/14/5.mrt differ diff --git a/debug/wind-512-mrt/4/14/6.mrt b/debug/wind-512-mrt/4/14/6.mrt new file mode 100644 index 00000000000..2102f893aa6 Binary files /dev/null and b/debug/wind-512-mrt/4/14/6.mrt differ diff --git a/debug/wind-512-mrt/5/26/11.mrt b/debug/wind-512-mrt/5/26/11.mrt new file mode 100644 index 00000000000..aca73987a57 Binary files /dev/null and b/debug/wind-512-mrt/5/26/11.mrt differ diff --git a/debug/wind-512-mrt/5/26/12.mrt b/debug/wind-512-mrt/5/26/12.mrt new file mode 100644 index 00000000000..f05799c2063 Binary files /dev/null and b/debug/wind-512-mrt/5/26/12.mrt differ diff --git a/debug/wind-512-mrt/5/26/13.mrt b/debug/wind-512-mrt/5/26/13.mrt new file mode 100644 index 00000000000..320cc236512 Binary files /dev/null and b/debug/wind-512-mrt/5/26/13.mrt differ diff --git a/debug/wind-512-mrt/5/27/11.mrt b/debug/wind-512-mrt/5/27/11.mrt new file mode 100644 index 00000000000..2acdbe2b838 Binary files /dev/null and b/debug/wind-512-mrt/5/27/11.mrt differ diff --git a/debug/wind-512-mrt/5/27/12.mrt b/debug/wind-512-mrt/5/27/12.mrt new file mode 100644 index 00000000000..35e92187f86 Binary files /dev/null and b/debug/wind-512-mrt/5/27/12.mrt differ diff --git a/debug/wind-512-mrt/5/27/13.mrt b/debug/wind-512-mrt/5/27/13.mrt new file mode 100644 index 00000000000..eef772271d6 Binary files /dev/null and b/debug/wind-512-mrt/5/27/13.mrt differ diff --git a/debug/wind-512-mrt/5/28/11.mrt b/debug/wind-512-mrt/5/28/11.mrt new file mode 100644 index 00000000000..edaa84bb96c Binary files /dev/null and b/debug/wind-512-mrt/5/28/11.mrt differ diff --git a/debug/wind-512-mrt/5/28/12.mrt b/debug/wind-512-mrt/5/28/12.mrt new file mode 100644 index 00000000000..d5be8988dc8 Binary files /dev/null and b/debug/wind-512-mrt/5/28/12.mrt differ diff --git a/debug/wind-512-mrt/5/28/13.mrt b/debug/wind-512-mrt/5/28/13.mrt new file mode 100644 index 00000000000..7829f6f2f5c Binary files /dev/null and b/debug/wind-512-mrt/5/28/13.mrt differ diff --git a/debug/wind-512-mrt/5/29/11.mrt b/debug/wind-512-mrt/5/29/11.mrt new file mode 100644 index 00000000000..42012d8b7e8 Binary files /dev/null and b/debug/wind-512-mrt/5/29/11.mrt differ diff --git a/debug/wind-512-mrt/5/29/12.mrt b/debug/wind-512-mrt/5/29/12.mrt new file mode 100644 index 00000000000..d1ed86e5e3b Binary files /dev/null and b/debug/wind-512-mrt/5/29/12.mrt differ diff --git a/debug/wind-512-mrt/5/29/13.mrt b/debug/wind-512-mrt/5/29/13.mrt new file mode 100644 index 00000000000..8efbb59f10f Binary files /dev/null and b/debug/wind-512-mrt/5/29/13.mrt differ diff --git a/debug/wind-512-mrt/tile.json b/debug/wind-512-mrt/tile.json new file mode 100644 index 00000000000..24f27a97c76 --- /dev/null +++ b/debug/wind-512-mrt/tile.json @@ -0,0 +1,49 @@ +{ + "tiles": [ + "http://localhost:9967/debug/wind-512-mrt/{z}/{x}/{y}.mrt" + ], + "raster_layers": [ + { + "fields": { + "range": [ + -6.97, + 37.27 + ], + "name": "wind", + "units": "m/s", + "tilesize": 512, + "buffer": 1, + "bands": [ + "1708300800", + "1708311600", + "1708322400", + "1708333200", + "1708344000", + "1708354800", + "1708365600", + "1708376400", + "1708387200", + "1708398000", + "1708408800", + "1708419600", + "1708430400", + "1708441200", + "1708452000", + "1708462800", + "1708473600", + "1708484400", + "1708495200", + "1708506000", + "1708516800", + "1708527600", + "1708538400", + "1708549200", + "1708560000", + "1708570800", + "1708581600" + ] + }, + "id": "wind" + } + ] +} \ No newline at end of file diff --git a/debug/wms.html b/debug/wms.html index 7ec9c851c1c..174fc15f44d 100644 --- a/debug/wms.html +++ b/debug/wms.html @@ -20,6 +20,7 @@ var map = window.map = new mapboxgl.Map({ container: 'map', + devtools: true, zoom: 16, center: [-74.0447, 40.6892], style: 'mapbox://styles/mapbox/streets-v9', diff --git a/flow-typed/gl-matrix.js b/flow-typed/gl-matrix.js index 4378b287681..f60da2e4770 100644 --- a/flow-typed/gl-matrix.js +++ b/flow-typed/gl-matrix.js @@ -114,6 +114,6 @@ declare module "gl-matrix" { rotateX(T, Quat, number): T, rotateY(T, Quat, number): T, rotateZ(T, Quat, number): T, - rotationTo(T, Quat, Quat): T + rotationTo(T, Quat, Quat): T } } diff --git a/flow-typed/grid-index.js b/flow-typed/grid-index.js deleted file mode 100644 index acea69120e5..00000000000 --- a/flow-typed/grid-index.js +++ /dev/null @@ -1,13 +0,0 @@ -// @flow strict -declare module 'grid-index' { - declare class GridIndex { - constructor(extent: number, n: number, padding: number): GridIndex; - constructor(data: ArrayBuffer): GridIndex; - - insert(key: number, x1: number, y1: number, x2: number, y2: number): void; - query(x1: number, y1: number, x2: number, y2: number, intersectionText?: (number, number, number, number) => boolean): Array; - toArrayBuffer(): ArrayBuffer; - } - - declare export default Class; -} diff --git a/flow-typed/intl.js b/flow-typed/intl.js new file mode 100644 index 00000000000..7d437bc0b84 --- /dev/null +++ b/flow-typed/intl.js @@ -0,0 +1,58 @@ +// @flow strict + +// Flow type declarations for Intl cribbed from +// https://github.com/facebook/flow/issues/1270 + +declare var Intl: { + NumberFormat: Class; + Collator: Class; +}; + +declare class Intl$NumberFormat { + constructor ( + locales?: string | string[], + options?: NumberFormatOptions + ): Intl$NumberFormat; + + static ( + locales?: string | string[], + options?: NumberFormatOptions + ): Intl$NumberFormat; + + format(a: number): string; + + resolvedOptions(): any; +} + +type NumberFormatOptions = { + style?: 'decimal' | 'currency' | 'percent' | 'unit'; + currency?: null | string; + unit?: null | string; + minimumFractionDigits?: null | string; + maximumFractionDigits?: null | string; +}; + +declare class Intl$Collator { + constructor ( + locales?: string | string[], + options?: CollatorOptions + ): Intl$Collator; + + static ( + locales?: string | string[], + options?: CollatorOptions + ): Intl$Collator; + + compare (a: string, b: string): number; + + resolvedOptions(): any; +} + +type CollatorOptions = { + localeMatcher?: 'lookup' | 'best fit', + usage?: 'sort' | 'search', + sensitivity?: 'base' | 'accent' | 'case' | 'variant', + ignorePunctuation?: boolean, + numeric?: boolean, + caseFirst?: 'upper' | 'lower' | 'false' +} diff --git a/flow-typed/jsdom.js b/flow-typed/jsdom.js deleted file mode 100644 index 7e3193b496b..00000000000 --- a/flow-typed/jsdom.js +++ /dev/null @@ -1,16 +0,0 @@ -// @flow strict - -declare module "jsdom" { - declare class JSDOM { - constructor(content: string, options: Object): JSDOM; - window: WindowProxy; - } - declare class VirtualConsole { - constructor(): VirtualConsole; - sendTo(console: typeof console): VirtualConsole; - } - declare module.exports: { - JSDOM: typeof JSDOM, - VirtualConsole: typeof VirtualConsole - }; -} diff --git a/flow-typed/sinon.js b/flow-typed/sinon.js deleted file mode 100644 index be0350bd521..00000000000 --- a/flow-typed/sinon.js +++ /dev/null @@ -1,28 +0,0 @@ -// @flow strict -declare module "sinon" { - declare type SpyCall = { - args: Array - }; - declare type Spy = { - (): any, - calledOnce: number, - getCall(i: number): SpyCall - }; - declare type Stub = { - callsFake(fn: mixed): Spy - }; - declare class FakeServer { - xhr: XMLHttpRequest - } - declare type Sandbox = { - xhr: {supportsCORS: boolean}, - fakeServer: {create: () => FakeServer}, - - createSandbox(options: mixed): Sandbox, - stub(obj?: mixed, prop?: string): Stub, - spy(obj?: mixed, prop?: string): Spy, - restore(): void; - }; - - declare module.exports: Sandbox; -} diff --git a/flow-typed/tracked_parameters_proxy.js b/flow-typed/tracked_parameters_proxy.js new file mode 100644 index 00000000000..08c30a027bf --- /dev/null +++ b/flow-typed/tracked_parameters_proxy.js @@ -0,0 +1,82 @@ +// @flow + +declare module 'tracked_parameters_proxy' { + declare type Description = any; + + declare function registerParameter( + containerObject: any, + scope: Array, + name: string, + description?: Description | null, + changeValueCallback?: any | null + ): void; + + declare function registerButton( + scope: Array, + buttonTitle: string, + onClick: any + ): void; + + declare function registerBinding( + containerObject: any, + scope: Array, + name: string, + description?: Object, + ): void; + + declare function refreshUI(): void; + + declare interface ITrackedParameters { + constructor(map: any): void; + + registerParameter( + containerObject: any, + scope: Array, + name: string, + description?: Description | null, + changeValueCallback?: any | null + ): void; + + registerButton( + scope: Array, + buttonTitle: string, + onClick: any + ): void; + + registerBinding( + containerObject: any, + scope: Array, + name: string, + description?: Object, + ): void; + + refreshUI(): void; + } + + declare class TrackedParameters implements ITrackedParameters { + constructor(map: any): void; + + registerParameter( + containerObject: any, + scope: Array, + name: string, + description?: Description | null, + changeValueCallback?: any | null + ): void; + + registerButton( + scope: Array, + buttonTitle: string, + onClick: any + ): void; + + registerBinding( + containerObject: any, + scope: Array, + name: string, + description?: Object, + ): void; + + refreshUI(): void; + } +} diff --git a/flow-typed/webgl2.js b/flow-typed/webgl2.js index 96d120e0cc8..f207e52babe 100644 --- a/flow-typed/webgl2.js +++ b/flow-typed/webgl2.js @@ -15,7 +15,18 @@ declare interface WebGLQuery { new(): WebGLQuery; } -export type WebGL2RenderingContext = WebGLRenderingContext & { +declare class WebGL2RenderingContext extends WebGLRenderingContext { + R8: 0x8229; + R32F: 0x822E; + RGBA16F: 0x881A; + RED: 0x1903; + HALF_FLOAT: 0x140B; + QUERY_RESULT: 0x8866; + MIN: 0x8007; + MAX: 0x8008; + INTERLEAVED_ATTRIBS: 0x8C8C; + SEPARATE_ATTRIBS: 0x8C8D; + createVertexArray: () => WebGLVertexArrayObject | null; deleteVertexArray: (vertexArray: WebGLVertexArrayObject | null) => void; bindVertexArray: (array: WebGLVertexArrayObject | null) => void; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000000..d0188cd755c --- /dev/null +++ b/package-lock.json @@ -0,0 +1,14184 @@ +{ + "name": "mapbox-gl", + "version": "3.4.0-beta.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "mapbox-gl", + "version": "3.4.0-beta.1", + "license": "SEE LICENSE IN LICENSE.txt", + "dependencies": { + "@mapbox/jsonlint-lines-primitives": "^2.0.2", + "@mapbox/mapbox-gl-supported": "^3.0.0", + "@mapbox/point-geometry": "^0.1.0", + "@mapbox/tiny-sdf": "^2.0.6", + "@mapbox/unitbezier": "^0.0.1", + "@mapbox/vector-tile": "^1.3.1", + "@mapbox/whoots-js": "^3.1.0", + "cheap-ruler": "^3.0.1", + "csscolorparser": "~1.0.3", + "earcut": "^2.2.4", + "fflate": "^0.8.1", + "geojson-vt": "^3.2.1", + "gl-matrix": "^3.4.3", + "grid-index": "^1.1.0", + "kdbush": "^4.0.1", + "lodash.clonedeep": "^4.5.0", + "murmurhash-js": "^1.0.0", + "pbf": "^3.2.1", + "potpack": "^2.0.0", + "quickselect": "^2.0.0", + "rw": "^1.3.3", + "serialize-to-js": "^3.1.2", + "supercluster": "^8.0.0", + "tiny-lru": "^11.2.6", + "tinyqueue": "^2.0.3", + "tweakpane": "^4.0.3", + "vt-pbf": "^3.1.3" + }, + "devDependencies": { + "@babel/core": "^7.24.5", + "@babel/eslint-parser": "^7.24.5", + "@mapbox/flow-remove-types": "^2.0.0", + "@mapbox/mvt-fixtures": "^3.10.0", + "@octokit/rest": "^20.1.1", + "@rollup/plugin-alias": "^5.1.0", + "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-json": "^6.1.0", + "@rollup/plugin-node-resolve": "^15.2.3", + "@rollup/plugin-replace": "^5.0.5", + "@rollup/plugin-strip": "^3.0.4", + "@rollup/plugin-terser": "^0.4.4", + "@vitest/browser": "^1.6.0", + "@vitest/ui": "^1.6.0", + "address": "^2.0.2", + "browserify": "^17.0.0", + "chalk": "^5.0.1", + "chokidar": "^3.6.0", + "cross-env": "^7.0.3", + "cssnano": "^7.0.1", + "d3-queue": "^3.0.7", + "diff": "^5.2.0", + "ejs": "^3.1.10", + "envify": "^4.1.0", + "eslint": "^7.32.0", + "eslint-config-mourner": "^3.0.0", + "eslint-plugin-flowtype": "^7.0.0", + "eslint-plugin-html": "^8.1.1", + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-jsdoc": "^48.2.3", + "flow-bin": "0.191.0", + "glob": "^10.3.12", + "is-builtin-module": "^4.0.0", + "jest-extended": "^4.0.2", + "json-stringify-pretty-compact": "^4.0.0", + "lodash.template": "^4.5.0", + "mapbox-gl-styles": "^2.0.2", + "minimist": "^1.2.6", + "mock-geolocation": "^1.0.11", + "msw": "^2.2.11", + "node-notifier": "^10.0.1", + "npm-font-open-sans": "^1.1.0", + "npm-run-all": "^4.1.5", + "pixelmatch": "^5.3.0", + "playwright": "^1.43.1", + "postcss": "^8.4.38", + "postcss-cli": "^11.0.0", + "postcss-inline-svg": "^6.0.0", + "pretty-bytes": "^6.0.0", + "puppeteer-core": "^22.8.0", + "qrcode-terminal": "^0.12.0", + "rollup": "3.29.4", + "rollup-plugin-unassert": "^0.6.0", + "serve-static": "^1.15.0", + "shuffle-seed": "^1.1.6", + "st": "^3.0.0", + "stylelint": "^16.5.0", + "stylelint-config-standard": "^36.0.0", + "tape": "^5.7.5", + "tape-filter": "^1.0.4", + "testem": "^3.13.0", + "vite-plugin-arraybuffer": "^0.0.7", + "vite-plugin-node-polyfills": "^0.21.0", + "vitest": "^1.6.0" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", + "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.24.2", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.1.tgz", + "integrity": "sha512-Pc65opHDliVpRHuKfzI+gSA4zcgr65O4cl64fFJIWEEh8JoHIHh0Oez1Eo8Arz8zq/JhgKodQaxEwUPRtZylVA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.5.tgz", + "integrity": "sha512-tVQRucExLQ02Boi4vdPp49svNGcfL2GhdTCT9aldhXgCJVAI21EtRfBettiuLUwce/7r6bFdgs6JFkcdTiFttA==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.24.2", + "@babel/generator": "^7.24.5", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.24.5", + "@babel/helpers": "^7.24.5", + "@babel/parser": "^7.24.5", + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.5", + "@babel/types": "^7.24.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/eslint-parser": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.24.5.tgz", + "integrity": "sha512-gsUcqS/fPlgAw1kOtpss7uhY6E9SFFANQ6EFX5GTvzUwaV0+sGaZWk6xq22MOdeT9wfxyokW3ceCUvOiRtZciQ==", + "dev": true, + "dependencies": { + "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", + "eslint-visitor-keys": "^2.1.0", + "semver": "^6.3.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || >=14.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0", + "eslint": "^7.5.0 || ^8.0.0 || ^9.0.0" + } + }, + "node_modules/@babel/generator": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.5.tgz", + "integrity": "sha512-x32i4hEXvr+iI0NEoEfDKzlemF8AmtOP8CcrRaEcpzysWuoEb1KknpcvMsHKPONoKZiDuItklgWhB18xEhr9PA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.5", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.24.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz", + "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.5.tgz", + "integrity": "sha512-9GxeY8c2d2mdQUP1Dye0ks3VDyIMS98kt/llQ2nUId8IsWqTF0l1LkSX0/uP7l7MCDrzXS009Hyhe2gzTiGW8A==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.24.3", + "@babel/helper-simple-access": "^7.24.5", + "@babel/helper-split-export-declaration": "^7.24.5", + "@babel/helper-validator-identifier": "^7.24.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.5.tgz", + "integrity": "sha512-uH3Hmf5q5n7n8mz7arjUlDOCbttY/DW4DYhE6FUsjKJ/oYC1kQQUvwEQWxRwUpX9qQKRXeqLwWxrqilMrf32sQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.5.tgz", + "integrity": "sha512-5CHncttXohrHk8GWOFCcCl4oRD9fKosWlIRgWm4ql9VYioKm52Mk2xsmoohvm7f3JoiLSM5ZgJuRaf5QZZYd3Q==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", + "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz", + "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.5.tgz", + "integrity": "sha512-CiQmBMMpMQHwM5m01YnrM6imUG1ebgYJ+fAIW4FZe6m4qHTPaRHti+R8cggAwkdz4oXhtO4/K9JWlh+8hIfR2Q==", + "dev": true, + "dependencies": { + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.5", + "@babel/types": "^7.24.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz", + "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.5.tgz", + "integrity": "sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", + "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.23.5", + "@babel/parser": "^7.24.0", + "@babel/types": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.5.tgz", + "integrity": "sha512-7aaBLeDQ4zYcUFDUD41lJc1fG8+5IU9DaNSJAgal866FGvmD5EbWQgnEC6kO1gGLsX0esNkfnJSndbTXA3r7UA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.24.2", + "@babel/generator": "^7.24.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.24.5", + "@babel/parser": "^7.24.5", + "@babel/types": "^7.24.5", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.5.tgz", + "integrity": "sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.24.1", + "@babel/helper-validator-identifier": "^7.24.5", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bundled-es-modules/cookie": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/cookie/-/cookie-2.0.0.tgz", + "integrity": "sha512-Or6YHg/kamKHpxULAdSqhGqnWFneIXu1NKvvfBBzKGwpVsYuFIQ5aBPHDnnoR3ghW1nvSkALd+EF9iMtY7Vjxw==", + "dev": true, + "dependencies": { + "cookie": "^0.5.0" + } + }, + "node_modules/@bundled-es-modules/statuses": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/statuses/-/statuses-1.0.1.tgz", + "integrity": "sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==", + "dev": true, + "dependencies": { + "statuses": "^2.0.1" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.6.1.tgz", + "integrity": "sha512-ubEkAaTfVZa+WwGhs5jbo5Xfqpeaybr/RvWzvFxRs4jfq16wH8l8Ty/QEEpINxll4xhuGfdMbipRyz5QZh9+FA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": "^14 || ^16 || >=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^2.2.4" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.2.4.tgz", + "integrity": "sha512-PuWRAewQLbDhGeTvFuq2oClaSCKPIBmHyIobCV39JHRYN0byDcUWJl5baPeNUcqrjtdMNqFooE0FGl31I3JOqw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": "^14 || ^16 || >=18" + } + }, + "node_modules/@csstools/media-query-list-parser": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.9.tgz", + "integrity": "sha512-qqGuFfbn4rUmyOB0u8CVISIp5FfJ5GAR3mBrZ9/TKndHakdnm6pY0L/fbLcpPnrzwCyyTEZl1nUcXAYHEWneTA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": "^14 || ^16 || >=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^2.6.1", + "@csstools/css-tokenizer": "^2.2.4" + } + }, + "node_modules/@csstools/selector-specificity": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-3.0.3.tgz", + "integrity": "sha512-KEPNw4+WW5AVEIyzC80rTbWEUatTW2lXpN8+8ILC8PiPeWPjwUzrPZDIOZ2wwqDmeqOYTdSGyL3+vE5GC3FB3Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": "^14 || ^16 || >=18" + }, + "peerDependencies": { + "postcss-selector-parser": "^6.0.13" + } + }, + "node_modules/@dual-bundle/import-meta-resolve": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@dual-bundle/import-meta-resolve/-/import-meta-resolve-4.0.0.tgz", + "integrity": "sha512-ZKXyJeFAzcpKM2kk8ipoGIPUqx9BX52omTGnfwjJvxOCaZTM2wtDK7zN0aIgPRbT9XYAlha0HtmZ+XKteuh0Gw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/@es-joy/jsdoccomment": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.42.0.tgz", + "integrity": "sha512-R1w57YlVA6+YE01wch3GPYn6bCsrOV3YW/5oGGE2tmX6JcL9Nr+b5IikrjMPF+v9CV3ay+obImEdsDhovhJrzw==", + "dev": true, + "dependencies": { + "comment-parser": "1.4.1", + "esquery": "^1.5.0", + "jsdoc-type-pratt-parser": "~4.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", + "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", + "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", + "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", + "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", + "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", + "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", + "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", + "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", + "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", + "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", + "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", + "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", + "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", + "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", + "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", + "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", + "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", + "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", + "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", + "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", + "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", + "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", + "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", + "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", + "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@inquirer/confirm": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-3.1.0.tgz", + "integrity": "sha512-nH5mxoTEoqk6WpoBz80GMpDSm9jH5V9AF8n+JZAZfMzd9gHeEG9w1o3KawPRR72lfzpP+QxBHLkOKLEApwhDiQ==", + "dev": true, + "dependencies": { + "@inquirer/core": "^7.1.0", + "@inquirer/type": "^1.2.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/core": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-7.1.0.tgz", + "integrity": "sha512-FRCiDiU54XHt5B/D8hX4twwZuzSP244ANHbu3R7CAsJfiv1dUOz24ePBgCZjygEjDUi6BWIJuk4eWLKJ7LATUw==", + "dev": true, + "dependencies": { + "@inquirer/type": "^1.2.1", + "@types/mute-stream": "^0.0.4", + "@types/node": "^20.11.26", + "@types/wrap-ansi": "^3.0.0", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "cli-spinners": "^2.9.2", + "cli-width": "^4.1.0", + "figures": "^3.2.0", + "mute-stream": "^1.0.0", + "run-async": "^3.0.0", + "signal-exit": "^4.1.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/core/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@inquirer/core/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@inquirer/core/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@inquirer/core/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@inquirer/core/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/@inquirer/core/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@inquirer/core/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@inquirer/type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-1.2.1.tgz", + "integrity": "sha512-xwMfkPAxeo8Ji/IxfUSqzRi0/+F2GIqJmpc5/thelgMGsjNZcjDDRBO9TLXT1s/hdx/mK5QbVIvgoLIFgXhTMQ==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@javascript-obfuscator/escodegen": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@javascript-obfuscator/escodegen/-/escodegen-2.3.0.tgz", + "integrity": "sha512-QVXwMIKqYMl3KwtTirYIA6gOCiJ0ZDtptXqAv/8KWLG9uQU2fZqTVy7a/A5RvcoZhbDoFfveTxuGxJ5ibzQtkw==", + "dev": true, + "dependencies": { + "@javascript-obfuscator/estraverse": "^5.3.0", + "esprima": "^4.0.1", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/@javascript-obfuscator/escodegen/node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@javascript-obfuscator/escodegen/node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@javascript-obfuscator/escodegen/node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@javascript-obfuscator/escodegen/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@javascript-obfuscator/escodegen/node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@javascript-obfuscator/estraverse": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@javascript-obfuscator/estraverse/-/estraverse-5.4.0.tgz", + "integrity": "sha512-CZFX7UZVN9VopGbjTx4UXaXsi9ewoM1buL0kY7j1ftYdSs7p2spv9opxFjHlQ/QGTgh4UqufYqJJ0WKLml7b6w==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@ljharb/resumer": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@ljharb/resumer/-/resumer-0.1.3.tgz", + "integrity": "sha512-d+tsDgfkj9X5QTriqM4lKesCkMMJC3IrbPKHvayP00ELx2axdXvDfWkqjxrLXIzGcQzmj7VAUT1wopqARTvafw==", + "dev": true, + "dependencies": { + "@ljharb/through": "^2.3.13", + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/@ljharb/through": { + "version": "2.3.13", + "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.13.tgz", + "integrity": "sha512-/gKJun8NNiWGZJkGzI/Ragc53cOdcLNdzjLaIa+GEjguQs0ulsurx8WN0jijdK9yPqDvziX995sMRLyLt1uZMQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/@mapbox/flow-remove-types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@mapbox/flow-remove-types/-/flow-remove-types-2.0.0.tgz", + "integrity": "sha512-FZIrEhfDJ4YUsedDrRJUptVg5gs1PJECdv8PzFEjgqNXn/dQBg0gXIvcsqNweuptybyGM+/Dl2E1QIvarxaMSw==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.12.15", + "node-modules-regexp": "^1.0.0", + "pirates": "^4.0.1", + "vlq": "^1.0.1" + }, + "bin": { + "flow-node": "flow-node", + "flow-remove-types": "flow-remove-types" + } + }, + "node_modules/@mapbox/jsonlint-lines-primitives": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz", + "integrity": "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@mapbox/mapbox-gl-supported": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-3.0.0.tgz", + "integrity": "sha512-2XghOwu16ZwPJLOFVuIOaLbN0iKMn867evzXFyf0P22dqugezfJwLmdanAgU25ITvz1TvOfVP4jsDImlDJzcWg==" + }, + "node_modules/@mapbox/mvt-fixtures": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/@mapbox/mvt-fixtures/-/mvt-fixtures-3.10.0.tgz", + "integrity": "sha512-HpObcr5eu7MOcxWqjj81fWjQ/VNUaAWKoK/rjxnd6NeEgN3uknrq6aGrkhC5vvZ20T2G6sWkikyITJ8mgPUa8g==", + "dev": true, + "dependencies": { + "@mapbox/sphericalmercator": "^1.0.5", + "@mapbox/vector-tile": "^1.3.0", + "d3-queue": "^3.0.7", + "pbf": "^3.0.5", + "protocol-buffers-schema": "^3.3.2" + } + }, + "node_modules/@mapbox/point-geometry": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz", + "integrity": "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ==" + }, + "node_modules/@mapbox/sphericalmercator": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@mapbox/sphericalmercator/-/sphericalmercator-1.2.0.tgz", + "integrity": "sha512-ZTOuuwGuMOJN+HEmG/68bSEw15HHaMWmQ5gdTsWdWsjDe56K1kGvLOK6bOSC8gWgIvEO0w6un/2Gvv1q5hJSkQ==", + "dev": true, + "bin": { + "bbox": "bin/bbox.js", + "to4326": "bin/to4326.js", + "to900913": "bin/to900913.js", + "xyz": "bin/xyz.js" + } + }, + "node_modules/@mapbox/tiny-sdf": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.0.6.tgz", + "integrity": "sha512-qMqa27TLw+ZQz5Jk+RcwZGH7BQf5G/TrutJhspsca/3SHwmgKQ1iq+d3Jxz5oysPVYTGP6aXxCo5Lk9Er6YBAA==" + }, + "node_modules/@mapbox/unitbezier": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz", + "integrity": "sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==" + }, + "node_modules/@mapbox/vector-tile": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-1.3.1.tgz", + "integrity": "sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw==", + "dependencies": { + "@mapbox/point-geometry": "~0.1.0" + } + }, + "node_modules/@mapbox/whoots-js": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz", + "integrity": "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@mswjs/cookies": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@mswjs/cookies/-/cookies-1.1.0.tgz", + "integrity": "sha512-0ZcCVQxifZmhwNBoQIrystCb+2sWBY2Zw8lpfJBPCHGCA/HWqehITeCRVIv4VMy8MPlaHo2w2pTHFV2pFfqKPw==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@mswjs/interceptors": { + "version": "0.25.16", + "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.25.16.tgz", + "integrity": "sha512-8QC8JyKztvoGAdPgyZy49c9vSHHAZjHagwl4RY9E8carULk8ym3iTaiawrT1YoLF/qb449h48f71XDPgkUSOUg==", + "dev": true, + "dependencies": { + "@open-draft/deferred-promise": "^2.2.0", + "@open-draft/logger": "^0.3.0", + "@open-draft/until": "^2.0.0", + "is-node-process": "^1.2.0", + "outvariant": "^1.2.1", + "strict-event-emitter": "^0.5.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { + "version": "5.1.1-v1", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", + "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==", + "dev": true, + "dependencies": { + "eslint-scope": "5.1.1" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@octokit/auth-token": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz", + "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==", + "dev": true, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/core": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.1.0.tgz", + "integrity": "sha512-BDa2VAMLSh3otEiaMJ/3Y36GU4qf6GI+VivQ/P41NC6GHcdxpKlqV0ikSZ5gdQsmS3ojXeRx5vasgNTinF0Q4g==", + "dev": true, + "dependencies": { + "@octokit/auth-token": "^4.0.0", + "@octokit/graphql": "^7.0.0", + "@octokit/request": "^8.0.2", + "@octokit/request-error": "^5.0.0", + "@octokit/types": "^12.0.0", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/endpoint": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.4.tgz", + "integrity": "sha512-DWPLtr1Kz3tv8L0UvXTDP1fNwM0S+z6EJpRcvH66orY6Eld4XBMCSYsaWp4xIm61jTWxK68BrR7ibO+vSDnZqw==", + "dev": true, + "dependencies": { + "@octokit/types": "^12.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/graphql": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.0.2.tgz", + "integrity": "sha512-OJ2iGMtj5Tg3s6RaXH22cJcxXRi7Y3EBqbHTBRq+PQAqfaS8f/236fUrWhfSn8P4jovyzqucxme7/vWSSZBX2Q==", + "dev": true, + "dependencies": { + "@octokit/request": "^8.0.1", + "@octokit/types": "^12.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz", + "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==", + "dev": true + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.3.1.tgz", + "integrity": "sha512-ryqobs26cLtM1kQxqeZui4v8FeznirUsksiA+RYemMPJ7Micju0WSkv50dBksTuZks9O5cg4wp+t8fZ/cLY56g==", + "dev": true, + "dependencies": { + "@octokit/types": "^13.5.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "5" + } + }, + "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/openapi-types": { + "version": "22.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz", + "integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==", + "dev": true + }, + "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": { + "version": "13.5.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.5.0.tgz", + "integrity": "sha512-HdqWTf5Z3qwDVlzCrP8UJquMwunpDiMPt5er+QjGzL4hqr/vBVY/MauQgS1xWxCDT1oMx1EULyqxncdCY/NVSQ==", + "dev": true, + "dependencies": { + "@octokit/openapi-types": "^22.2.0" + } + }, + "node_modules/@octokit/plugin-request-log": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-4.0.1.tgz", + "integrity": "sha512-GihNqNpGHorUrO7Qa9JbAl0dbLnqJVrV8OXe2Zm5/Y4wFkZQDfTreBzVmiRfJVfE4mClXdihHnbpyyO9FSX4HA==", + "dev": true, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "5" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-13.2.2.tgz", + "integrity": "sha512-EI7kXWidkt3Xlok5uN43suK99VWqc8OaIMktY9d9+RNKl69juoTyxmLoWPIZgJYzi41qj/9zU7G/ljnNOJ5AFA==", + "dev": true, + "dependencies": { + "@octokit/types": "^13.5.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "^5" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/openapi-types": { + "version": "22.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz", + "integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==", + "dev": true + }, + "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": { + "version": "13.5.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.5.0.tgz", + "integrity": "sha512-HdqWTf5Z3qwDVlzCrP8UJquMwunpDiMPt5er+QjGzL4hqr/vBVY/MauQgS1xWxCDT1oMx1EULyqxncdCY/NVSQ==", + "dev": true, + "dependencies": { + "@octokit/openapi-types": "^22.2.0" + } + }, + "node_modules/@octokit/request": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.2.0.tgz", + "integrity": "sha512-exPif6x5uwLqv1N1irkLG1zZNJkOtj8bZxuVHd71U5Ftuxf2wGNvAJyNBcPbPC+EBzwYEbBDdSFb8EPcjpYxPQ==", + "dev": true, + "dependencies": { + "@octokit/endpoint": "^9.0.0", + "@octokit/request-error": "^5.0.0", + "@octokit/types": "^12.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/request-error": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.0.1.tgz", + "integrity": "sha512-X7pnyTMV7MgtGmiXBwmO6M5kIPrntOXdyKZLigNfQWSEQzVxR4a4vo49vJjTWX70mPndj8KhfT4Dx+2Ng3vnBQ==", + "dev": true, + "dependencies": { + "@octokit/types": "^12.0.0", + "deprecation": "^2.0.0", + "once": "^1.4.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/rest": { + "version": "20.1.1", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-20.1.1.tgz", + "integrity": "sha512-MB4AYDsM5jhIHro/dq4ix1iWTLGToIGk6cWF5L6vanFaMble5jTX/UBQyiv05HsWnwUtY8JrfHy2LWfKwihqMw==", + "dev": true, + "dependencies": { + "@octokit/core": "^5.0.2", + "@octokit/plugin-paginate-rest": "11.3.1", + "@octokit/plugin-request-log": "^4.0.0", + "@octokit/plugin-rest-endpoint-methods": "13.2.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/types": { + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz", + "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==", + "dev": true, + "dependencies": { + "@octokit/openapi-types": "^20.0.0" + } + }, + "node_modules/@open-draft/deferred-promise": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", + "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==", + "dev": true + }, + "node_modules/@open-draft/logger": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz", + "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==", + "dev": true, + "dependencies": { + "is-node-process": "^1.2.0", + "outvariant": "^1.4.0" + } + }, + "node_modules/@open-draft/until": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz", + "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==", + "dev": true + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.25", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.25.tgz", + "integrity": "sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==", + "dev": true + }, + "node_modules/@puppeteer/browsers": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.2.3.tgz", + "integrity": "sha512-bJ0UBsk0ESOs6RFcLXOt99a3yTDcOKlzfjad+rhFwdaG1Lu/Wzq58GHYCDTlZ9z6mldf4g+NTb+TXEfe0PpnsQ==", + "dev": true, + "dependencies": { + "debug": "4.3.4", + "extract-zip": "2.0.1", + "progress": "2.0.3", + "proxy-agent": "6.4.0", + "semver": "7.6.0", + "tar-fs": "3.0.5", + "unbzip2-stream": "1.4.3", + "yargs": "17.7.2" + }, + "bin": { + "browsers": "lib/cjs/main-cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@puppeteer/browsers/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@puppeteer/browsers/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@puppeteer/browsers/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/@rollup/plugin-alias": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-alias/-/plugin-alias-5.1.0.tgz", + "integrity": "sha512-lpA3RZ9PdIG7qqhEfv79tBffNaoDuukFDrmhLqg9ifv99u/ehn+lOg30x2zmhf8AQqQUZaMk/B9fZraQ6/acDQ==", + "dev": true, + "dependencies": { + "slash": "^4.0.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-commonjs": { + "version": "25.0.7", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.7.tgz", + "integrity": "sha512-nEvcR+LRjEjsaSsc4x3XZfCCvZIaSMenZu/OiwOKGN2UhQpAYI7ru7czFvyWbErlpoGjnSX3D5Ch5FcMA3kRWQ==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "commondir": "^1.0.1", + "estree-walker": "^2.0.2", + "glob": "^8.0.3", + "is-reference": "1.2.1", + "magic-string": "^0.30.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.68.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-commonjs/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@rollup/plugin-commonjs/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@rollup/plugin-commonjs/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@rollup/plugin-inject": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@rollup/plugin-inject/-/plugin-inject-5.0.5.tgz", + "integrity": "sha512-2+DEJbNBoPROPkgTDNe8/1YXWcqxbN5DTjASVIOx8HS+pITXushyNiBV56RB08zuptzz8gT3YfkqriTBVycepg==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-json": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz", + "integrity": "sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^5.1.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "15.2.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz", + "integrity": "sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-builtin-module": "^3.2.1", + "is-module": "^1.0.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.78.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-node-resolve/node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@rollup/plugin-node-resolve/node_modules/is-builtin-module": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "dev": true, + "dependencies": { + "builtin-modules": "^3.3.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@rollup/plugin-replace": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-5.0.5.tgz", + "integrity": "sha512-rYO4fOi8lMaTg/z5Jb+hKnrHHVn8j2lwkqwyS4kTRhKyWOLf2wST2sWXr4WzWiTcoHTp2sTjqUbqIj2E39slKQ==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "magic-string": "^0.30.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-strip": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@rollup/plugin-strip/-/plugin-strip-3.0.4.tgz", + "integrity": "sha512-LDRV49ZaavxUo2YoKKMQjCxzCxugu1rCPQa0lDYBOWLj6vtzBMr8DcoJjsmg+s450RbKbe3qI9ZLaSO+O1oNbg==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-terser": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-0.4.4.tgz", + "integrity": "sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==", + "dev": true, + "dependencies": { + "serialize-javascript": "^6.0.1", + "smob": "^1.0.0", + "terser": "^5.17.4" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", + "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz", + "integrity": "sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.0.tgz", + "integrity": "sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.0.tgz", + "integrity": "sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.0.tgz", + "integrity": "sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.0.tgz", + "integrity": "sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.0.tgz", + "integrity": "sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.0.tgz", + "integrity": "sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.0.tgz", + "integrity": "sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.0.tgz", + "integrity": "sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.0.tgz", + "integrity": "sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.0.tgz", + "integrity": "sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.0.tgz", + "integrity": "sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.0.tgz", + "integrity": "sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", + "dev": true + }, + "node_modules/@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", + "dev": true + }, + "node_modules/@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "dev": true + }, + "node_modules/@types/cors": { + "version": "2.8.17", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", + "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "node_modules/@types/mute-stream": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@types/mute-stream/-/mute-stream-0.0.4.tgz", + "integrity": "sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "20.11.30", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz", + "integrity": "sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/resolve": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", + "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", + "dev": true + }, + "node_modules/@types/statuses": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.5.tgz", + "integrity": "sha512-jmIUGWrAiwu3dZpxntxieC+1n/5c3mjrImkmOSQ2NC5uP6cYO4aAZDdSmRcI5C1oiTmqlZGHC+/NmJrKogbP5A==", + "dev": true + }, + "node_modules/@types/wrap-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz", + "integrity": "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==", + "dev": true + }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "dev": true, + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@vitest/browser": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/browser/-/browser-1.6.0.tgz", + "integrity": "sha512-3Wpp9h1hf++rRVPvoXevkdHybLhJVn7MwIMKMIh08tVaoDMmT6fnNhbP222Z48V9PptpYeA5zvH9Ct/ZcaAzmQ==", + "dev": true, + "dependencies": { + "@vitest/utils": "1.6.0", + "magic-string": "^0.30.5", + "sirv": "^2.0.4" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "playwright": "*", + "vitest": "1.6.0", + "webdriverio": "*" + }, + "peerDependenciesMeta": { + "playwright": { + "optional": true + }, + "safaridriver": { + "optional": true + }, + "webdriverio": { + "optional": true + } + } + }, + "node_modules/@vitest/expect": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.6.0.tgz", + "integrity": "sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==", + "dev": true, + "dependencies": { + "@vitest/spy": "1.6.0", + "@vitest/utils": "1.6.0", + "chai": "^4.3.10" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.6.0.tgz", + "integrity": "sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg==", + "dev": true, + "dependencies": { + "@vitest/utils": "1.6.0", + "p-limit": "^5.0.0", + "pathe": "^1.1.1" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner/node_modules/p-limit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", + "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vitest/runner/node_modules/yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vitest/snapshot": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.6.0.tgz", + "integrity": "sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==", + "dev": true, + "dependencies": { + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.6.0.tgz", + "integrity": "sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==", + "dev": true, + "dependencies": { + "tinyspy": "^2.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/ui": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-1.6.0.tgz", + "integrity": "sha512-k3Lyo+ONLOgylctiGovRKy7V4+dIN2yxstX3eY5cWFXH6WP+ooVX79YSyi0GagdTQzLmT43BF27T0s6dOIPBXA==", + "dev": true, + "dependencies": { + "@vitest/utils": "1.6.0", + "fast-glob": "^3.3.2", + "fflate": "^0.8.1", + "flatted": "^3.2.9", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "sirv": "^2.0.4" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "vitest": "1.6.0" + } + }, + "node_modules/@vitest/utils": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.6.0.tgz", + "integrity": "sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==", + "dev": true, + "dependencies": { + "diff-sequences": "^29.6.3", + "estree-walker": "^3.0.3", + "loupe": "^2.3.7", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/@xmldom/xmldom": { + "version": "0.8.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", + "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-node": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", + "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", + "dev": true, + "dependencies": { + "acorn": "^7.0.0", + "acorn-walk": "^7.0.0", + "xtend": "^4.0.2" + } + }, + "node_modules/acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/address": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/address/-/address-2.0.2.tgz", + "integrity": "sha512-u6nFssvaX9RHQmjMSqgT7b7QJbf/5/U8+ntbTL8vgABfIiEmm02ZSM5MwljKjCrIrm7iIbgYEya2YW6AaRccVA==", + "dev": true, + "engines": { + "node": ">= 16.0.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "dev": true, + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==", + "dev": true, + "engines": { + "node": ">=0.4.2" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "dev": true + }, + "node_modules/are-docs-informative": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz", + "integrity": "sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/are-we-there-yet": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "dev": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/are-we-there-yet/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true + }, + "node_modules/array-includes": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/array.prototype.every": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/array.prototype.every/-/array.prototype.every-1.1.6.tgz", + "integrity": "sha512-gNEqZD97w6bfQRNmHkFv7rNnGM+VWyHZT+h/rf9C+22owcXuENr66Lfo0phItpU5KoXW6Owb34q2+8MnSIZ57w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-object-atoms": "^1.0.0", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", + "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", + "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.2.1", + "get-intrinsic": "^1.2.3", + "is-array-buffer": "^3.0.4", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "dev": true, + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/asn1.js/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/assert": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.1.tgz", + "integrity": "sha512-zzw1uCAgLbsKwBfFc8CX78DDg+xZeBksSO3vwVIDDN5i94eOrPsSSyiVhmsSABFDM/OcpE2aagCat9dnWQLG1A==", + "dev": true, + "dependencies": { + "object.assign": "^4.1.4", + "util": "^0.10.4" + } + }, + "node_modules/assert/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true + }, + "node_modules/assert/node_modules/util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "dev": true, + "dependencies": { + "inherits": "2.0.3" + } + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "dev": true, + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", + "dev": true + }, + "node_modules/async-cache": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/async-cache/-/async-cache-1.1.0.tgz", + "integrity": "sha512-YDQc4vBn5NFhY6g6HhVshyi3Fy9+SQ5ePnE7JLDJn1DoL+i7ER+vMwtTNOYk9leZkYMnOwpBCWqyLDPw8Aig8g==", + "deprecated": "No longer maintained. Use [lru-cache](http://npm.im/lru-cache) version 7.6 or higher, and provide an asynchronous `fetchMethod` option.", + "dev": true, + "dependencies": { + "lru-cache": "^4.0.0" + } + }, + "node_modules/async-cache/node_modules/lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "dependencies": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "node_modules/async-cache/node_modules/yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", + "dev": true + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/b4a": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.6.tgz", + "integrity": "sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==", + "dev": true + }, + "node_modules/backbone": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/backbone/-/backbone-1.6.0.tgz", + "integrity": "sha512-13PUjmsgw/49EowNcQvfG4gmczz1ximTMhUktj0Jfrjth0MVaTxehpU+qYYX4MxnuIuhmvBLC6/ayxuAGnOhbA==", + "dev": true, + "dependencies": { + "underscore": ">=1.8.3" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/bare-events": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.2.2.tgz", + "integrity": "sha512-h7z00dWdG0PYOQEvChhOSWvOfkIKsdZGkWr083FgN/HyoQuebSew/cgirYqh9SCuy/hRvxc5Vy6Fw8xAmYHLkQ==", + "dev": true, + "optional": true + }, + "node_modules/bare-fs": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.0.tgz", + "integrity": "sha512-TNFqa1B4N99pds2a5NYHR15o0ZpdNKbAeKTE/+G6ED/UeOavv8RY3dr/Fu99HW3zU3pXpo2kDNO8Sjsm2esfOw==", + "dev": true, + "optional": true, + "dependencies": { + "bare-events": "^2.0.0", + "bare-path": "^2.0.0", + "bare-stream": "^1.0.0" + } + }, + "node_modules/bare-os": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.3.0.tgz", + "integrity": "sha512-oPb8oMM1xZbhRQBngTgpcQ5gXw6kjOaRsSWsIeNyRxGed2w/ARyP7ScBYpWR1qfX2E5rS3gBw6OWcSQo+s+kUg==", + "dev": true, + "optional": true + }, + "node_modules/bare-path": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-2.1.2.tgz", + "integrity": "sha512-o7KSt4prEphWUHa3QUwCxUI00R86VdjiuxmJK0iNVDHYPGo+HsDaVCnqCmPbf/MiW1ok8F4p3m8RTHlWk8K2ig==", + "dev": true, + "optional": true, + "dependencies": { + "bare-os": "^2.1.0" + } + }, + "node_modules/bare-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-1.0.0.tgz", + "integrity": "sha512-KhNUoDL40iP4gFaLSsoGE479t0jHijfYdIcxRn/XtezA2BaUD0NRf/JGRpsMq6dMNM+SrCrB0YSSo/5wBY4rOQ==", + "dev": true, + "optional": true, + "dependencies": { + "streamx": "^2.16.1" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "dev": true, + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/basic-ftp": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", + "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/before-after-hook": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", + "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bl": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", + "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==", + "dev": true, + "dependencies": { + "buffer": "^6.0.3", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/bl/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + }, + "node_modules/body-parser": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", + "dev": true + }, + "node_modules/browser-pack": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.1.0.tgz", + "integrity": "sha512-erYug8XoqzU3IfcU8fUgyHqyOXqIE4tUTTQ+7mqUjQlvnXkOO6OlT9c/ZoJVHYoAaqGxr09CN53G7XIsO4KtWA==", + "dev": true, + "dependencies": { + "combine-source-map": "~0.8.0", + "defined": "^1.0.0", + "JSONStream": "^1.0.3", + "safe-buffer": "^5.1.1", + "through2": "^2.0.0", + "umd": "^3.0.0" + }, + "bin": { + "browser-pack": "bin/cmd.js" + } + }, + "node_modules/browser-resolve": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-2.0.0.tgz", + "integrity": "sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ==", + "dev": true, + "dependencies": { + "resolve": "^1.17.0" + } + }, + "node_modules/browserify": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/browserify/-/browserify-17.0.0.tgz", + "integrity": "sha512-SaHqzhku9v/j6XsQMRxPyBrSP3gnwmE27gLJYZgMT2GeK3J0+0toN+MnuNYDfHwVGQfLiMZ7KSNSIXHemy905w==", + "dev": true, + "dependencies": { + "assert": "^1.4.0", + "browser-pack": "^6.0.1", + "browser-resolve": "^2.0.0", + "browserify-zlib": "~0.2.0", + "buffer": "~5.2.1", + "cached-path-relative": "^1.0.0", + "concat-stream": "^1.6.0", + "console-browserify": "^1.1.0", + "constants-browserify": "~1.0.0", + "crypto-browserify": "^3.0.0", + "defined": "^1.0.0", + "deps-sort": "^2.0.1", + "domain-browser": "^1.2.0", + "duplexer2": "~0.1.2", + "events": "^3.0.0", + "glob": "^7.1.0", + "has": "^1.0.0", + "htmlescape": "^1.1.0", + "https-browserify": "^1.0.0", + "inherits": "~2.0.1", + "insert-module-globals": "^7.2.1", + "JSONStream": "^1.0.3", + "labeled-stream-splicer": "^2.0.0", + "mkdirp-classic": "^0.5.2", + "module-deps": "^6.2.3", + "os-browserify": "~0.3.0", + "parents": "^1.0.1", + "path-browserify": "^1.0.0", + "process": "~0.11.0", + "punycode": "^1.3.2", + "querystring-es3": "~0.2.0", + "read-only-stream": "^2.0.0", + "readable-stream": "^2.0.2", + "resolve": "^1.1.4", + "shasum-object": "^1.0.0", + "shell-quote": "^1.6.1", + "stream-browserify": "^3.0.0", + "stream-http": "^3.0.0", + "string_decoder": "^1.1.1", + "subarg": "^1.0.0", + "syntax-error": "^1.1.1", + "through2": "^2.0.0", + "timers-browserify": "^1.0.1", + "tty-browserify": "0.0.1", + "url": "~0.11.0", + "util": "~0.12.0", + "vm-browserify": "^1.0.0", + "xtend": "^4.0.0" + }, + "bin": { + "browserify": "bin/cmd.js" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "dependencies": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "node_modules/browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/browserify-rsa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "dev": true, + "dependencies": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "node_modules/browserify-sign": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.3.tgz", + "integrity": "sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw==", + "dev": true, + "dependencies": { + "bn.js": "^5.2.1", + "browserify-rsa": "^4.1.0", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.5", + "hash-base": "~3.0", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.7", + "readable-stream": "^2.3.8", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "dependencies": { + "pako": "~1.0.5" + } + }, + "node_modules/browserify/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/browserslist": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz", + "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==", + "dev": true, + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", + "dev": true + }, + "node_modules/builtin-modules": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-4.0.0.tgz", + "integrity": "sha512-p1n8zyCkt1BVrKNFymOHjcDSAl7oq/gUvfgULv2EblgpPVQlQr9yHnWjg9IJ2MhfwPqiYqMMrr01OY7yQoK2yA==", + "dev": true, + "engines": { + "node": ">=18.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==", + "dev": true + }, + "node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cached-path-relative": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.1.0.tgz", + "integrity": "sha512-WF0LihfemtesFcJgO7xfOoOcnWzY/QHR4qeDqV44jPU3HTI54+LnfXK3SA27AVVGCdZFgjjFFaqUA9Jx7dMJZA==", + "dev": true + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "dev": true, + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/caniuse-api/node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001600", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001600.tgz", + "integrity": "sha512-+2S9/2JFhYmYaDpZvo0lKkfvuKIglrx68MwOBqMGHhQsNkLjB5xtc/TGoEPs+MxjSyN/72qer2g97nzR641mOQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chai": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz", + "integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==", + "dev": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.0.8" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/charm": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/charm/-/charm-1.0.2.tgz", + "integrity": "sha512-wqW3VdPnlSWT4eRiYX+hcs+C6ViBPUWk1qTCd+37qw9kEm/a5n2qcyQDMBWvSYKN/ctqZzeXNQaeBjOetJJUkw==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1" + } + }, + "node_modules/cheap-ruler": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/cheap-ruler/-/cheap-ruler-3.0.2.tgz", + "integrity": "sha512-02T332h1/HTN6cDSufLP8x4JzDs2+VC+8qZ/N0kWIVPyc2xUkWwWh3B2fJxR7raXkL4Mq7k554mfuM9ofv/vGg==" + }, + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chromium-bidi": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.5.19.tgz", + "integrity": "sha512-UA6zL77b7RYCjJkZBsZ0wlvCTD+jTjllZ8f6wdO4buevXgTZYjV+XLB9CiEa2OuuTGGTLnI7eN9I60YxuALGQg==", + "dev": true, + "dependencies": { + "mitt": "3.0.1", + "urlpattern-polyfill": "10.0.0", + "zod": "3.22.4" + }, + "peerDependencies": { + "devtools-protocol": "*" + } + }, + "node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cliui/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/cliui/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true, + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", + "dev": true + }, + "node_modules/combine-source-map": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.8.0.tgz", + "integrity": "sha512-UlxQ9Vw0b/Bt/KYwCFqdEwsQ1eL8d1gibiFb7lxQJFdvTgc2hIZi6ugsg+kyhzhPV+QEpUiEIwInIAIrgoEkrg==", + "dev": true, + "dependencies": { + "convert-source-map": "~1.1.0", + "inline-source-map": "~0.6.0", + "lodash.memoize": "~3.0.3", + "source-map": "~0.5.3" + } + }, + "node_modules/combine-source-map/node_modules/convert-source-map": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz", + "integrity": "sha512-Y8L5rp6jo+g9VEPgvqNfEopjTR4OTYct8lXlS8iVQdmnjDvbdbzYe9rjtFCB9egC86JoNCU61WRY+ScjkZpnIg==", + "dev": true + }, + "node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/comment-parser": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.1.tgz", + "integrity": "sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==", + "dev": true, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "dependencies": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/compression/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/confbox": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.7.tgz", + "integrity": "sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==", + "dev": true + }, + "node_modules/console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", + "dev": true + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "dev": true + }, + "node_modules/consolidate": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.16.0.tgz", + "integrity": "sha512-Nhl1wzCslqXYTJVDyJCu3ODohy9OfBMB5uD2BiBTzd7w+QY0lBzafkR8y8755yMYHAaMD4NuzbAw03/xzfw+eQ==", + "deprecated": "Please upgrade to consolidate v1.0.0+ as it has been modernized with several long-awaited fixes implemented. Maintenance is supported by Forward Email at https://forwardemail.net ; follow/watch https://github.com/ladjs/consolidate for updates and release changelog", + "dev": true, + "dependencies": { + "bluebird": "^3.7.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==", + "dev": true + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "dev": true + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cosmiconfig": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "dev": true, + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/cosmiconfig/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/cosmiconfig/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/cosmiconfig/node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + } + }, + "node_modules/create-ecdh/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "dependencies": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + }, + "engines": { + "node": "*" + } + }, + "node_modules/css-declaration-sorter": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.2.0.tgz", + "integrity": "sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==", + "dev": true, + "engines": { + "node": "^14 || ^16 || >=18" + }, + "peerDependencies": { + "postcss": "^8.0.9" + } + }, + "node_modules/css-functions-list": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.2.2.tgz", + "integrity": "sha512-c+N0v6wbKVxTu5gOBBFkr9BEdBWaqqjQeiJ8QvSRIJOf+UxlJh930m8e6/WNeODIK0mYLFkoONrnj16i2EcvfQ==", + "dev": true, + "engines": { + "node": ">=12 || >=16" + } + }, + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "dev": true, + "dependencies": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/csscolorparser": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/csscolorparser/-/csscolorparser-1.0.3.tgz", + "integrity": "sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w==" + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-7.0.1.tgz", + "integrity": "sha512-917Mej/4SdI7b55atsli3sU4MOJ9XDoKgnlCtQtXYj8XUFcM3riTuYHyqBBnnskawW+zWwp0KxJzpEUodlpqUg==", + "dev": true, + "dependencies": { + "cssnano-preset-default": "^7.0.1", + "lilconfig": "^3.1.1" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/cssnano" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/cssnano-preset-default": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-7.0.1.tgz", + "integrity": "sha512-Fumyr+uZMcjYQeuHssAZxn0cKj3cdQc5GcxkBcmEzISGB+UW9CLNlU4tBOJbJGcPukFDlicG32eFbrc8K9V5pw==", + "dev": true, + "dependencies": { + "browserslist": "^4.23.0", + "css-declaration-sorter": "^7.2.0", + "cssnano-utils": "^5.0.0", + "postcss-calc": "^10.0.0", + "postcss-colormin": "^7.0.0", + "postcss-convert-values": "^7.0.0", + "postcss-discard-comments": "^7.0.0", + "postcss-discard-duplicates": "^7.0.0", + "postcss-discard-empty": "^7.0.0", + "postcss-discard-overridden": "^7.0.0", + "postcss-merge-longhand": "^7.0.0", + "postcss-merge-rules": "^7.0.0", + "postcss-minify-font-values": "^7.0.0", + "postcss-minify-gradients": "^7.0.0", + "postcss-minify-params": "^7.0.0", + "postcss-minify-selectors": "^7.0.0", + "postcss-normalize-charset": "^7.0.0", + "postcss-normalize-display-values": "^7.0.0", + "postcss-normalize-positions": "^7.0.0", + "postcss-normalize-repeat-style": "^7.0.0", + "postcss-normalize-string": "^7.0.0", + "postcss-normalize-timing-functions": "^7.0.0", + "postcss-normalize-unicode": "^7.0.0", + "postcss-normalize-url": "^7.0.0", + "postcss-normalize-whitespace": "^7.0.0", + "postcss-ordered-values": "^7.0.0", + "postcss-reduce-initial": "^7.0.0", + "postcss-reduce-transforms": "^7.0.0", + "postcss-svgo": "^7.0.0", + "postcss-unique-selectors": "^7.0.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/cssnano-utils": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-5.0.0.tgz", + "integrity": "sha512-Uij0Xdxc24L6SirFr25MlwC2rCFX6scyUmuKpzI+JQ7cyqDEwD42fJ0xfB3yLfOnRDU5LKGgjQ9FA6LYh76GWQ==", + "dev": true, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/csso": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", + "dev": true, + "dependencies": { + "css-tree": "~2.2.0" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "dev": true, + "dependencies": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", + "dev": true + }, + "node_modules/d3-queue": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/d3-queue/-/d3-queue-3.0.7.tgz", + "integrity": "sha512-2rs+6pNFKkrJhqe1rg5znw7dKJ7KZr62j9aLZfhondkrnz6U7VRmJj1UGcbD8MRc46c7H8m4SWhab8EalBQrkw==", + "dev": true + }, + "node_modules/dash-ast": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dash-ast/-/dash-ast-1.0.0.tgz", + "integrity": "sha512-Vy4dx7gquTeMcQR/hDkYLGUnwVil6vk4FOOct+djUnHOUWt+zJPJAaRIXaAFkPXtJjvlY7o3rfRu0/3hpnwoUA==", + "dev": true + }, + "node_modules/data-uri-to-buffer": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", + "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", + "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", + "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", + "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-eql": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/deep-equal": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", + "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.5", + "es-get-iterator": "^1.1.3", + "get-intrinsic": "^1.2.2", + "is-arguments": "^1.1.1", + "is-array-buffer": "^3.0.2", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "isarray": "^2.0.5", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "side-channel": "^1.0.4", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/deep-equal/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/defined": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz", + "integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/degenerator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", + "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "dev": true, + "dependencies": { + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "dev": true + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dependency-graph": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", + "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", + "dev": true + }, + "node_modules/deps-sort": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/deps-sort/-/deps-sort-2.0.1.tgz", + "integrity": "sha512-1orqXQr5po+3KI6kQb9A4jnXT1PBwggGl2d7Sq2xsnOeI9GPcE/tGcF9UiSZtZBM7MukY4cAh7MemS6tZYipfw==", + "dev": true, + "dependencies": { + "JSONStream": "^1.0.3", + "shasum-object": "^1.0.0", + "subarg": "^1.0.0", + "through2": "^2.0.0" + }, + "bin": { + "deps-sort": "bin/cmd.js" + } + }, + "node_modules/des.js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", + "integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detective": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz", + "integrity": "sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==", + "dev": true, + "dependencies": { + "acorn-node": "^1.8.2", + "defined": "^1.0.0", + "minimist": "^1.2.6" + }, + "bin": { + "detective": "bin/detective.js" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/devtools-protocol": { + "version": "0.0.1273771", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1273771.tgz", + "integrity": "sha512-QDbb27xcTVReQQW/GHJsdQqGKwYBE7re7gxehj467kKP2DKuYBUj6i2k5LRiAC66J1yZG/9gsxooz/s9pcm0Og==", + "dev": true + }, + "node_modules/diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "node_modules/diffie-hellman/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dir-glob/node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true, + "engines": { + "node": ">=0.4", + "npm": ">=1.2" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "dev": true, + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dotignore": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/dotignore/-/dotignore-0.1.2.tgz", + "integrity": "sha512-UGGGWfSauusaVJC+8fgV+NVvBXkCTmVv7sk6nojDZZvuOUNGUy0Zk4UpHQD6EDjS0jpBwcACvH4eofvyzBcRDw==", + "dev": true, + "dependencies": { + "minimatch": "^3.0.4" + }, + "bin": { + "ignored": "bin/ignored" + } + }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/earcut": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz", + "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==" + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.715", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.715.tgz", + "integrity": "sha512-XzWNH4ZSa9BwVUQSDorPWAUQ5WGuYz7zJUNpNif40zFCiCl20t8zgylmreNmn26h5kiyw2lg7RfTmeMBsDklqg==", + "dev": true + }, + "node_modules/elliptic": { + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.5.tgz", + "integrity": "sha512-7EjbcmUm17NQFu4Pmgmq2olYMj8nwMnpcddByChSUjArp8F5DQWcIcpriwO4ZToLNAJig0yiyjswfyGNje/ixw==", + "dev": true, + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/engine.io": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.4.tgz", + "integrity": "sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg==", + "dev": true, + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.11.0" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz", + "integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", + "dev": true + }, + "node_modules/engine.io/node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/enquirer": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/envify": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/envify/-/envify-4.1.0.tgz", + "integrity": "sha512-IKRVVoAYr4pIx4yIWNsz9mOsboxlNXiu7TNBnem/K/uTHdkyzXWDzHCK7UTolqBbgaBz0tQHsD3YNls0uIIjiw==", + "dev": true, + "dependencies": { + "esprima": "^4.0.0", + "through": "~2.3.4" + }, + "bin": { + "envify": "bin/envify" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.2.tgz", + "integrity": "sha512-60s3Xv2T2p1ICykc7c+DNDPLDMm9t4QxCOUU0K9JxiLjM3C1zB9YVdN7tjxrFd4+AkZ8CdX1ovUga4P2+1e+/w==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.3", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "data-view-buffer": "^1.0.1", + "data-view-byte-length": "^1.0.1", + "data-view-byte-offset": "^1.0.0", + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.0.3", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.4", + "get-symbol-description": "^1.0.2", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", + "has-symbols": "^1.0.3", + "hasown": "^2.0.2", + "internal-slot": "^1.0.7", + "is-array-buffer": "^3.0.4", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.1", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.3", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.13", + "is-weakref": "^1.0.2", + "object-inspect": "^1.13.1", + "object-keys": "^1.1.1", + "object.assign": "^4.1.5", + "regexp.prototype.flags": "^1.5.2", + "safe-array-concat": "^1.1.2", + "safe-regex-test": "^1.0.3", + "string.prototype.trim": "^1.2.9", + "string.prototype.trimend": "^1.0.8", + "string.prototype.trimstart": "^1.0.7", + "typed-array-buffer": "^1.0.2", + "typed-array-byte-length": "^1.0.1", + "typed-array-byte-offset": "^1.0.2", + "typed-array-length": "^1.0.5", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.15" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-get-iterator": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", + "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "is-arguments": "^1.1.1", + "is-map": "^2.0.2", + "is-set": "^2.0.2", + "is-string": "^1.0.7", + "isarray": "^2.0.5", + "stop-iteration-iterator": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-get-iterator/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/esbuild": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", + "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.20.2", + "@esbuild/android-arm": "0.20.2", + "@esbuild/android-arm64": "0.20.2", + "@esbuild/android-x64": "0.20.2", + "@esbuild/darwin-arm64": "0.20.2", + "@esbuild/darwin-x64": "0.20.2", + "@esbuild/freebsd-arm64": "0.20.2", + "@esbuild/freebsd-x64": "0.20.2", + "@esbuild/linux-arm": "0.20.2", + "@esbuild/linux-arm64": "0.20.2", + "@esbuild/linux-ia32": "0.20.2", + "@esbuild/linux-loong64": "0.20.2", + "@esbuild/linux-mips64el": "0.20.2", + "@esbuild/linux-ppc64": "0.20.2", + "@esbuild/linux-riscv64": "0.20.2", + "@esbuild/linux-s390x": "0.20.2", + "@esbuild/linux-x64": "0.20.2", + "@esbuild/netbsd-x64": "0.20.2", + "@esbuild/openbsd-x64": "0.20.2", + "@esbuild/sunos-x64": "0.20.2", + "@esbuild/win32-arm64": "0.20.2", + "@esbuild/win32-ia32": "0.20.2", + "@esbuild/win32-x64": "0.20.2" + } + }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/escodegen/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/escodegen/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint": { + "version": "7.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", + "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.3", + "@humanwhocodes/config-array": "^0.5.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.1.2", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.9", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-mourner": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-mourner/-/eslint-config-mourner-3.0.0.tgz", + "integrity": "sha512-QWMt3Cbqkhg/73fZ2UrTNa/p27nF3JhI1Ej2Jg7qSBri88Y0bg4LFzz0/6I5IrvFR10c6UPwDS+DsV9Ec42aVQ==", + "dev": true + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz", + "integrity": "sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==", + "dev": true, + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-flowtype": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-7.0.0.tgz", + "integrity": "sha512-kW3eipG2Vth6e0apYqmFs05IHhFklJJNokYNiNEO5AIjm7H29oTDybYNB2bMULUYcf7iX7Wf3GdRhfBORKcT1g==", + "dev": true, + "dependencies": { + "lodash": "^4.17.21", + "string-natural-compare": "^3.0.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "eslint": "^7.32.0" + } + }, + "node_modules/eslint-plugin-html": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-html/-/eslint-plugin-html-8.1.1.tgz", + "integrity": "sha512-6qmlJsc40D2m3Dn9oEH+0PAOkJhxVu0f5sVItqpCE0YWgYnyP4xCjBc3UWTHaJcY9ARkWOLIIuXLq0ndRnQOHw==", + "dev": true, + "dependencies": { + "htmlparser2": "^9.1.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", + "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.7", + "array.prototype.findlastindex": "^1.2.3", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.8.0", + "hasown": "^2.0.0", + "is-core-module": "^2.13.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.7", + "object.groupby": "^1.0.1", + "object.values": "^1.1.7", + "semver": "^6.3.1", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-jsdoc": { + "version": "48.2.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-48.2.3.tgz", + "integrity": "sha512-r9DMAmFs66VNvNqRLLjHejdnJtILrt3xGi+Qx0op0oRfFGVpOR1Hb3BC++MacseHx93d8SKYPhyrC9BS7Os2QA==", + "dev": true, + "dependencies": { + "@es-joy/jsdoccomment": "~0.42.0", + "are-docs-informative": "^0.0.2", + "comment-parser": "1.4.1", + "debug": "^4.3.4", + "escape-string-regexp": "^4.0.0", + "esquery": "^1.5.0", + "is-builtin-module": "^3.2.1", + "semver": "^7.6.0", + "spdx-expression-parse": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/is-builtin-module": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "dev": true, + "dependencies": { + "builtin-modules": "^3.3.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint/node_modules/@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.10.4" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "dependencies": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/events-to-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/events-to-array/-/events-to-array-1.1.2.tgz", + "integrity": "sha512-inRWzRY7nG+aXZxBzEqYKB3HPgwflZRopAjDCHv0whhRx+MTUr1ei0ICZUypdyE0HRm4L2d5VEcIqLD6yl+BFA==", + "dev": true + }, + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "dependencies": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/execa/node_modules/cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/execa/node_modules/get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/execa/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/execa/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/execa/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/execa/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/express": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", + "dev": true, + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.2", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.6.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/express/node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", + "dev": true + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extract-zip/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "dev": true + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fd": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/fd/-/fd-0.0.3.tgz", + "integrity": "sha512-iAHrIslQb3U68OcMSP0kkNWabp7sSN6d2TBSb2JO3gcLJVDd4owr/hKM4SFJovFOUeeXeItjYgouEDTMWiVAnA==", + "dev": true + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==" + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/figures/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fireworm": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/fireworm/-/fireworm-0.7.2.tgz", + "integrity": "sha512-GjebTzq+NKKhfmDxjKq3RXwQcN9xRmZWhnnuC9L+/x5wBQtR0aaQM50HsjrzJ2wc28v1vSdfOpELok0TKR4ddg==", + "dev": true, + "dependencies": { + "async": "~0.2.9", + "is-type": "0.0.1", + "lodash.debounce": "^3.1.1", + "lodash.flatten": "^3.0.2", + "minimatch": "^3.0.2" + } + }, + "node_modules/fireworm/node_modules/async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ==", + "dev": true + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, + "node_modules/flow-bin": { + "version": "0.191.0", + "resolved": "https://registry.npmjs.org/flow-bin/-/flow-bin-0.191.0.tgz", + "integrity": "sha512-IhaDGoOtRDdGUJLjm7KQlHK8BAzOVJmpx+CIR6bCju4pF7zon2v7WNrds5706WZqDE3rD2c8cM4GdhDnIFYXtg==", + "dev": true, + "bin": { + "flow": "cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "dev": true + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "dev": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/gauge/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/gauge/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/gauge/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/geojson-vt": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-3.2.1.tgz", + "integrity": "sha512-EvGQQi/zPrDA6zr6BnJD/YhwAkBP8nnJ9emh3EnHQKVMfg/MRVtPbMYdgVy/IaEmn4UfagD2a6fafPDL5hbtwg==" + }, + "node_modules/get-assigned-identifiers": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz", + "integrity": "sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ==", + "dev": true + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stdin": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz", + "integrity": "sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", + "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-uri": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.3.tgz", + "integrity": "sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==", + "dev": true, + "dependencies": { + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^6.0.2", + "debug": "^4.3.4", + "fs-extra": "^11.2.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/gl-matrix": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.3.tgz", + "integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==" + }, + "node_modules/glob": { + "version": "10.3.12", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", + "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.6", + "minimatch": "^9.0.1", + "minipass": "^7.0.4", + "path-scurry": "^1.10.2" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dev": true, + "dependencies": { + "global-prefix": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dev": true, + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.1.tgz", + "integrity": "sha512-jOMLD2Z7MAhyG8aJpNOpmziMOP4rPLcc95oQPKXBazW82z+CEgPFBQvEpRUa1KeIMUJo4Wsm+q6uzO/Q/4BksQ==", + "dev": true, + "dependencies": { + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.2", + "ignore": "^5.2.4", + "path-type": "^5.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/globby/node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globjoin": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz", + "integrity": "sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==", + "dev": true + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/graphql": { + "version": "16.8.1", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.8.1.tgz", + "integrity": "sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, + "node_modules/grid-index": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/grid-index/-/grid-index-1.1.0.tgz", + "integrity": "sha512-HZRwumpOGUrHyxO5bqKZL0B0GlUpwtCAzZ42sgxUPniu33R1LSFH5yrIcBCHjkctCAh3mtWKcKd9J4vDDdeVHA==" + }, + "node_modules/growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw==", + "dev": true + }, + "node_modules/has": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz", + "integrity": "sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-dynamic-import": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/has-dynamic-import/-/has-dynamic-import-2.1.0.tgz", + "integrity": "sha512-su0anMkNEnJKZ/rB99jn3y6lV/J8Ro96hBJ28YAeVzj5rWxH+YL/AdCyiYYA1HDLV9YhmvqpWSJJj2KLo1MX6g==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "get-intrinsic": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "dev": true + }, + "node_modules/hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha512-EeeoJKjTyt868liAlVmcv2ZsUfGHlE3Q+BICOXcZiwN3osr5Q/zFGYmTJpoIzuaSTAwndFy+GqhEwlU4L3j4Ow==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/headers-polyfill": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-4.0.3.tgz", + "integrity": "sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==", + "dev": true + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "dev": true, + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/html-tags": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", + "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/htmlescape": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz", + "integrity": "sha512-eVcrzgbR4tim7c7soKQKtxa/kQM4TzjnlU83rcZ9bHU6t31ehfV7SktN6McWgwPWg+JYMA/O3qpGxBvFq1z2Jg==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/htmlparser2": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", + "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "entities": "^4.5.0" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==", + "dev": true + }, + "node_modules/https-proxy-agent": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", + "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", + "dev": true, + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "node_modules/inline-source-map": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.3.tgz", + "integrity": "sha512-1aVsPEsJWMJq/pdMU61CDlm1URcW702MTB4w9/zUjMus6H/Py8o7g68Pr9D4I6QluWGt/KdmswuRhaA05xVR1w==", + "dev": true, + "dependencies": { + "source-map": "~0.5.3" + } + }, + "node_modules/insert-module-globals": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.2.1.tgz", + "integrity": "sha512-ufS5Qq9RZN+Bu899eA9QCAYThY+gGW7oRkmb0vC93Vlyu/CFGcH0OYPEjVkDXA5FEbTt1+VWzdoOD3Ny9N+8tg==", + "dev": true, + "dependencies": { + "acorn-node": "^1.5.2", + "combine-source-map": "^0.8.0", + "concat-stream": "^1.6.1", + "is-buffer": "^1.1.0", + "JSONStream": "^1.0.3", + "path-is-absolute": "^1.0.1", + "process": "~0.11.0", + "through2": "^2.0.0", + "undeclared-identifiers": "^1.1.2", + "xtend": "^4.0.0" + }, + "bin": { + "insert-module-globals": "bin/cmd.js" + } + }, + "node_modules/internal-slot": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", + "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.0", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "dev": true, + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/ip-address/node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", + "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "node_modules/is-builtin-module": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-4.0.0.tgz", + "integrity": "sha512-rWP3AMAalQSesXO8gleROyL2iKU73SX5Er66losQn9rWOWL4Gef0a/xOEOVqjWGMuR2vHG3FJ8UUmT700O8oFg==", + "dev": true, + "dependencies": { + "builtin-modules": "^4.0.0" + }, + "engines": { + "node": ">=18.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", + "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "dev": true, + "dependencies": { + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "dev": true + }, + "node_modules/is-nan": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", + "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-node-process": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz", + "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==", + "dev": true + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-reference": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", + "dev": true, + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-type": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/is-type/-/is-type-0.0.1.tgz", + "integrity": "sha512-YwJh/zBVrcJ90aAnPBM0CbHvm7lG9ao7lIFeqTZ1UQj4iFLpM5CikdaU+dGGesrMJwxLqPGmjjrUrQ6Kn3Zh+w==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "dev": true, + "dependencies": { + "which-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz", + "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/isomorphic-timers-promises": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/isomorphic-timers-promises/-/isomorphic-timers-promises-1.0.1.tgz", + "integrity": "sha512-u4sej9B1LPSxTGKB/HiuzvEQnXH0ECYkSVQU39koSwmFAxhlEAFl9RdTvLv4TOTQUgBS5O3O5fwUxk6byBZ+IQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jake": { + "version": "10.8.7", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz", + "integrity": "sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==", + "dev": true, + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jake/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jake/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jake/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jake/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-diff/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-diff/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-diff/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-diff/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-extended": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/jest-extended/-/jest-extended-4.0.2.tgz", + "integrity": "sha512-FH7aaPgtGYHc9mRjriS0ZEHYM5/W69tLrFTIdzm+yJgeoCmmrSB/luSfMSqWP9O29QWHPEmJ4qmU6EwsZideog==", + "dev": true, + "dependencies": { + "jest-diff": "^29.0.0", + "jest-get-type": "^29.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "jest": ">=27.2.5" + }, + "peerDependenciesMeta": { + "jest": { + "optional": true + } + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "dev": true + }, + "node_modules/jsdoc-type-pratt-parser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.0.0.tgz", + "integrity": "sha512-YtOli5Cmzy3q4dP26GraSOeAhqecewG04hoO8DY56CH4KJ9Fvv5qKWUCCo3HZob7esJQHCv6/+bnTy72xZZaVQ==", + "dev": true, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json-stringify-pretty-compact": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-4.0.0.tgz", + "integrity": "sha512-3CNZ2DnrpByG9Nqj6Xo8vqbjT4F6N+tb4Gb28ESAZjYZ5yqvmc56J+/kuIwkaAMOyblTQhUW7PxMkUb8Q36N3Q==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true, + "engines": [ + "node >= 0.2.0" + ] + }, + "node_modules/JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "dependencies": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + }, + "bin": { + "JSONStream": "bin.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/kdbush": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-4.0.2.tgz", + "integrity": "sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA==" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/known-css-properties": { + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.30.0.tgz", + "integrity": "sha512-VSWXYUnsPu9+WYKkfmJyLKtIvaRJi1kXUqVmBACORXZQxT5oZDsoZ2vQP+bQFDnWtpI/4eq3MLoRMjI2fnLzTQ==", + "dev": true + }, + "node_modules/labeled-stream-splicer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-2.0.2.tgz", + "integrity": "sha512-Ca4LSXFFZUjPScRaqOcFxneA0VpKZr4MMYCljyQr4LIewTLb3Y0IUTIsnBBsVubIeEfxeSZpSjSsRM8APEQaAw==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "stream-splicer": "^2.0.0" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lilconfig": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz", + "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/local-pkg": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz", + "integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==", + "dev": true, + "dependencies": { + "mlly": "^1.4.2", + "pkg-types": "^1.0.3" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash._baseflatten": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/lodash._baseflatten/-/lodash._baseflatten-3.1.4.tgz", + "integrity": "sha512-fESngZd+X4k+GbTxdMutf8ohQa0s3sJEHIcwtu4/LsIQ2JTDzdRxDCMQjW+ezzwRitLmHnacVVmosCbxifefbw==", + "dev": true, + "dependencies": { + "lodash.isarguments": "^3.0.0", + "lodash.isarray": "^3.0.0" + } + }, + "node_modules/lodash._getnative": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", + "integrity": "sha512-RrL9VxMEPyDMHOd9uFbvMe8X55X16/cGM5IgOKgRElQZutpX89iS6vwl64duTV1/16w5JY7tuFNXqoekmh1EmA==", + "dev": true + }, + "node_modules/lodash._isiterateecall": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", + "integrity": "sha512-De+ZbrMu6eThFti/CSzhRvTKMgQToLxbij58LMfM8JnYDNSOjkjTCIaa8ixglOeGh2nyPlakbt5bJWJ7gvpYlQ==", + "dev": true + }, + "node_modules/lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA==", + "dev": true + }, + "node_modules/lodash.assignin": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz", + "integrity": "sha512-yX/rx6d/UTVh7sSVWVSIMjfnz95evAgDFdb1ZozC35I9mSFCkmzptOzevxjgbQUsc78NR44LVHWjsoMQXy9FDg==", + "dev": true + }, + "node_modules/lodash.castarray": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz", + "integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==", + "dev": true + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" + }, + "node_modules/lodash.debounce": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-3.1.1.tgz", + "integrity": "sha512-lcmJwMpdPAtChA4hfiwxTtgFeNAaow701wWUgVUqeD0XJF7vMXIN+bu/2FJSGxT0NUbZy9g9VFrlOFfPjl+0Ew==", + "dev": true, + "dependencies": { + "lodash._getnative": "^3.0.0" + } + }, + "node_modules/lodash.find": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.find/-/lodash.find-4.6.0.tgz", + "integrity": "sha512-yaRZoAV3Xq28F1iafWN1+a0rflOej93l1DQUejs3SZ41h2O9UJBoS9aueGjPDgAl4B6tPC0NuuchLKaDQQ3Isg==", + "dev": true + }, + "node_modules/lodash.flatten": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-3.0.2.tgz", + "integrity": "sha512-jCXLoNcqQRbnT/KWZq2fIREHWeczrzpTR0vsycm96l/pu5hGeAntVBG0t7GuM/2wFqmnZs3d1eGptnAH2E8+xQ==", + "dev": true, + "dependencies": { + "lodash._baseflatten": "^3.0.0", + "lodash._isiterateecall": "^3.0.0" + } + }, + "node_modules/lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==", + "dev": true + }, + "node_modules/lodash.isarray": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", + "integrity": "sha512-JwObCrNJuT0Nnbuecmqr5DgtuBppuCvGD9lxjFpAzwnVtdGoDQ1zig+5W8k5/6Gcn0gZ3936HDAlGd28i7sOGQ==", + "dev": true + }, + "node_modules/lodash.memoize": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz", + "integrity": "sha512-eDn9kqrAmVUC1wmZvlQ6Uhde44n+tXpqPrN8olQJbttgh0oKclk+SF54P47VEGE9CEiMeRwAP8BaM7UHvBkz2A==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lodash.template": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", + "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", + "dev": true, + "dependencies": { + "lodash._reinterpolate": "^3.0.0", + "lodash.templatesettings": "^4.0.0" + } + }, + "node_modules/lodash.templatesettings": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", + "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", + "dev": true, + "dependencies": { + "lodash._reinterpolate": "^3.0.0" + } + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "dev": true + }, + "node_modules/lodash.uniqby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", + "integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==", + "dev": true + }, + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.1" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.30.8", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", + "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/mapbox-gl-styles": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mapbox-gl-styles/-/mapbox-gl-styles-2.0.2.tgz", + "integrity": "sha512-Uf9Pd37vnKK+7iVs5PHvVr1k0cCdHu+k+twYx0woFf12y6nq340qJUb2HtBFu8vzYjx4OFBvLES8GQZ5ei5sLw==", + "deprecated": "This package has moved to the @mapbox namespace. All new version are available via @mapbox/mapbox-gl-styles", + "dev": true, + "dependencies": { + "glob": "^5.0.14" + } + }, + "node_modules/mapbox-gl-styles/node_modules/glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA==", + "dev": true, + "dependencies": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mathml-tag-names": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz", + "integrity": "sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "dev": true + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/meow": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", + "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", + "dev": true + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "dependencies": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "bin": { + "miller-rabin": "bin/miller-rabin" + } + }, + "node_modules/miller-rabin/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", + "dev": true + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "dev": true + }, + "node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true, + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true + }, + "node_modules/mlly": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.6.1.tgz", + "integrity": "sha512-vLgaHvaeunuOXHSmEbZ9izxPx3USsk8KCQ8iC+aTlp5sKRSoZvwhHh5L9VbKSaVC6sJDqbyohIS76E2VmHIPAA==", + "dev": true, + "dependencies": { + "acorn": "^8.11.3", + "pathe": "^1.1.2", + "pkg-types": "^1.0.3", + "ufo": "^1.3.2" + } + }, + "node_modules/mlly/node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/mock-geolocation": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/mock-geolocation/-/mock-geolocation-1.0.11.tgz", + "integrity": "sha512-F/kvZfwuVnuPNHjHPuSVZlch8HnLwZgq7LVyp83PKSW3sXYm3tJhi/Z1gIHvnbY953YfAxiq5a7wFhgzX+qIkg==", + "dev": true + }, + "node_modules/mock-property": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/mock-property/-/mock-property-1.0.3.tgz", + "integrity": "sha512-2emPTb1reeLLYwHxyVx993iYyCHEiRRO+y8NFXFPL5kl5q14sgTK76cXyEKkeKCHeRw35SfdkUJ10Q1KfHuiIQ==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.1", + "functions-have-names": "^1.2.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "hasown": "^2.0.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mock-property/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/module-deps": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-6.2.3.tgz", + "integrity": "sha512-fg7OZaQBcL4/L+AK5f4iVqf9OMbCclXfy/znXRxTVhJSeW5AIlS9AwheYwDaXM3lVW7OBeaeUEY3gbaC6cLlSA==", + "dev": true, + "dependencies": { + "browser-resolve": "^2.0.0", + "cached-path-relative": "^1.0.2", + "concat-stream": "~1.6.0", + "defined": "^1.0.0", + "detective": "^5.2.0", + "duplexer2": "^0.1.2", + "inherits": "^2.0.1", + "JSONStream": "^1.0.3", + "parents": "^1.0.0", + "readable-stream": "^2.0.2", + "resolve": "^1.4.0", + "stream-combiner2": "^1.1.1", + "subarg": "^1.0.0", + "through2": "^2.0.0", + "xtend": "^4.0.0" + }, + "bin": { + "module-deps": "bin/cmd.js" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/mrmime": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", + "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/msw": { + "version": "2.2.11", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.2.11.tgz", + "integrity": "sha512-XtIoewF7XWLT0a39Ftkazt9PprBA1bxHZ4CSlomN74sCBJOJU2w5VwLmGlswwsOBhGoF7jovt6bxrSIESxA1KA==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@bundled-es-modules/cookie": "^2.0.0", + "@bundled-es-modules/statuses": "^1.0.1", + "@inquirer/confirm": "^3.0.0", + "@mswjs/cookies": "^1.1.0", + "@mswjs/interceptors": "^0.25.16", + "@open-draft/until": "^2.1.0", + "@types/cookie": "^0.6.0", + "@types/statuses": "^2.0.4", + "chalk": "^4.1.2", + "graphql": "^16.8.1", + "headers-polyfill": "^4.0.2", + "is-node-process": "^1.2.0", + "outvariant": "^1.4.2", + "path-to-regexp": "^6.2.0", + "strict-event-emitter": "^0.5.1", + "type-fest": "^4.9.0", + "yargs": "^17.7.2" + }, + "bin": { + "msw": "cli/index.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mswjs" + }, + "peerDependencies": { + "typescript": ">= 4.7.x" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/msw/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/msw/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/msw/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/msw/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/msw/node_modules/path-to-regexp": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz", + "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==", + "dev": true + }, + "node_modules/multi-stage-sourcemap": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/multi-stage-sourcemap/-/multi-stage-sourcemap-0.3.1.tgz", + "integrity": "sha512-UiTLYjqeIoVnJHyWGskwMKIhtZKK9uXUjSTWuwatarrc0d2H/6MAVFdwvEA/aKOHamIn7z4tfvxjz+FYucFpNQ==", + "dev": true, + "dependencies": { + "source-map": "^0.1.34" + } + }, + "node_modules/multi-stage-sourcemap/node_modules/source-map": { + "version": "0.1.43", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", + "integrity": "sha512-VtCvB9SIQhk3aF6h+N85EaqIaBFIAfZ9Cu+NJHHVvc8BbEcnvDcFw6sqQ2dQrT6SlOrZq3tIvyD9+EGq/lJryQ==", + "dev": true, + "dependencies": { + "amdefine": ">=0.0.4" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/murmurhash-js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz", + "integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==" + }, + "node_modules/mustache": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", + "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", + "dev": true, + "bin": { + "mustache": "bin/mustache" + } + }, + "node_modules/mute-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", + "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/netmask": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node_modules/node-modules-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", + "integrity": "sha512-JMaRS9L4wSRIR+6PTVEikTrq/lMGEZR43a48ETeilY0Q0iMwVnccMFrUM1k+tNzmYuIU0Vh710bCUqHX+/+ctQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/node-notifier": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-10.0.1.tgz", + "integrity": "sha512-YX7TSyDukOZ0g+gmzjB6abKu+hTGvO8+8+gIFDsRCU2t8fLV/P2unmt+LGFaIa4y64aX98Qksa97rgz4vMNeLQ==", + "dev": true, + "dependencies": { + "growly": "^1.3.0", + "is-wsl": "^2.2.0", + "semver": "^7.3.5", + "shellwords": "^0.1.1", + "uuid": "^8.3.2", + "which": "^2.0.2" + } + }, + "node_modules/node-notifier/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-notifier/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-notifier/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "dev": true + }, + "node_modules/node-stdlib-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/node-stdlib-browser/-/node-stdlib-browser-1.2.0.tgz", + "integrity": "sha512-VSjFxUhRhkyed8AtLwSCkMrJRfQ3e2lGtG3sP6FEgaLKBBbxM/dLfjRe1+iLhjvyLFW3tBQ8+c0pcOtXGbAZJg==", + "dev": true, + "dependencies": { + "assert": "^2.0.0", + "browser-resolve": "^2.0.0", + "browserify-zlib": "^0.2.0", + "buffer": "^5.7.1", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "create-require": "^1.1.1", + "crypto-browserify": "^3.11.0", + "domain-browser": "^4.22.0", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "isomorphic-timers-promises": "^1.0.1", + "os-browserify": "^0.3.0", + "path-browserify": "^1.0.1", + "pkg-dir": "^5.0.0", + "process": "^0.11.10", + "punycode": "^1.4.1", + "querystring-es3": "^0.2.1", + "readable-stream": "^3.6.0", + "stream-browserify": "^3.0.0", + "stream-http": "^3.2.0", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.1", + "url": "^0.11.0", + "util": "^0.12.4", + "vm-browserify": "^1.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-stdlib-browser/node_modules/assert": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", + "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "is-nan": "^1.3.2", + "object-is": "^1.1.5", + "object.assign": "^4.1.4", + "util": "^0.12.5" + } + }, + "node_modules/node-stdlib-browser/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/node-stdlib-browser/node_modules/domain-browser": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-4.23.0.tgz", + "integrity": "sha512-ArzcM/II1wCCujdCNyQjXrAFwS4mrLh4C7DZWlaI8mdh7h3BfKdNd3bKXITfl2PT9FtfQqaGvhi1vPRQPimjGA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/node-stdlib-browser/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/node-stdlib-browser/node_modules/timers-browserify": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", + "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", + "dev": true, + "dependencies": { + "setimmediate": "^1.0.4" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-font-open-sans": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/npm-font-open-sans/-/npm-font-open-sans-1.1.0.tgz", + "integrity": "sha512-t1y5ShWm6a8FSLwBdINT47XYMcuKY2rkTBsTdz/76YB2MtX0YD89RUkY2eSS2/XOmlZfBe1HFBAwD+b9+/UfmQ==", + "dev": true + }, + "node_modules/npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + }, + "bin": { + "npm-run-all": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm-run-all/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/npm-run-all/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/npm-run-all/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/npm-run-all/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-all/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-all/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", + "dev": true, + "dependencies": { + "path-key": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "dev": true, + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-is": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.values": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", + "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==", + "dev": true + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/outvariant": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.2.tgz", + "integrity": "sha512-Ou3dJ6bA/UJ5GVHxah4LnqDwZRwAmWxrG3wtrHrbGnP4RnLCtA64A4F+ae7Y8ww660JaddSoArUR5HjipWSHAQ==", + "dev": true + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pac-proxy-agent": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.1.tgz", + "integrity": "sha512-ASV8yU4LLKBAjqIPMbrgtaKIvxQri/yh2OpI+S6hVa9JRkUI3Y3NPFbfngDtY7oFtSMD3w31Xns89mDa3Feo5A==", + "dev": true, + "dependencies": { + "@tootallnate/quickjs-emscripten": "^0.23.0", + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "get-uri": "^6.0.1", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.2", + "pac-resolver": "^7.0.0", + "socks-proxy-agent": "^8.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-resolver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", + "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", + "dev": true, + "dependencies": { + "degenerator": "^5.0.0", + "netmask": "^2.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parents": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz", + "integrity": "sha512-mXKF3xkoUt5td2DoxpLmtOmZvko9VfFpwRwkKDHSNvgmpLAeBo18YDhcPbBzJq+QLCHMbGOfzia2cX4U+0v9Mg==", + "dev": true, + "dependencies": { + "path-platform": "~0.11.15" + } + }, + "node_modules/parse-asn1": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.7.tgz", + "integrity": "sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==", + "dev": true, + "dependencies": { + "asn1.js": "^4.10.1", + "browserify-aes": "^1.2.0", + "evp_bytestokey": "^1.0.3", + "hash-base": "~3.0", + "pbkdf2": "^3.1.2", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-platform": { + "version": "0.11.15", + "resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz", + "integrity": "sha512-Y30dB6rab1A/nfEKsZxmr01nUotHX0c/ZiIAsCTatEe1CmS5Pm5He7fZ195bPT7RdquoaL8lLxFCMQi/bS7IJg==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/path-scurry": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz", + "integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", + "dev": true, + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/path-type": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz", + "integrity": "sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/pbf": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.2.1.tgz", + "integrity": "sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ==", + "dependencies": { + "ieee754": "^1.1.12", + "resolve-protobuf-schema": "^2.1.0" + }, + "bin": { + "pbf": "bin/pbf" + } + }, + "node_modules/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dev": true, + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", + "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", + "dev": true, + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pixelmatch": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-5.3.0.tgz", + "integrity": "sha512-o8mkY4E/+LNUf6LzX96ht6k6CEDi65k9G2rjMtBe9Oo+VPKSvl+0GKHuH/AlG+GA5LPG/i5hrekkxUc3s2HU+Q==", + "dev": true, + "dependencies": { + "pngjs": "^6.0.0" + }, + "bin": { + "pixelmatch": "bin/pixelmatch" + } + }, + "node_modules/pkg-dir": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-5.0.0.tgz", + "integrity": "sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==", + "dev": true, + "dependencies": { + "find-up": "^5.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/pkg-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.1.0.tgz", + "integrity": "sha512-/RpmvKdxKf8uILTtoOhAgf30wYbP2Qw+L9p3Rvshx1JZVX+XQNZQFjlbmGHEGIm4CkVPlSn+NXmIM8+9oWQaSA==", + "dev": true, + "dependencies": { + "confbox": "^0.1.7", + "mlly": "^1.6.1", + "pathe": "^1.1.2" + } + }, + "node_modules/playwright": { + "version": "1.43.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.43.1.tgz", + "integrity": "sha512-V7SoH0ai2kNt1Md9E3Gwas5B9m8KR2GVvwZnAI6Pg0m3sh7UvgiYhRrhsziCmqMJNouPckiOhk8T+9bSAK0VIA==", + "dev": true, + "dependencies": { + "playwright-core": "1.43.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.43.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.43.1.tgz", + "integrity": "sha512-EI36Mto2Vrx6VF7rm708qSnesVQKbxEWvPrfA1IPY6HgczBplDx7ENtx+K2n4kJ41sLLkuGfmb0ZLSSXlDhqPg==", + "dev": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/pngjs": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-6.0.0.tgz", + "integrity": "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==", + "dev": true, + "engines": { + "node": ">=12.13.0" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-calc": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-10.0.0.tgz", + "integrity": "sha512-OmjhudoNTP0QleZCwl1i6NeBwN+5MZbY5ersLZz69mjJiDVv/p57RjRuKDkHeDWr4T+S97wQfsqRTNoDHB2e3g==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.16", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12 || ^20.9 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.38" + } + }, + "node_modules/postcss-cli": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/postcss-cli/-/postcss-cli-11.0.0.tgz", + "integrity": "sha512-xMITAI7M0u1yolVcXJ9XTZiO9aO49mcoKQy6pCDFdMh9kGqhzLVpWxeD/32M/QBmkhcGypZFFOLNLmIW4Pg4RA==", + "dev": true, + "dependencies": { + "chokidar": "^3.3.0", + "dependency-graph": "^0.11.0", + "fs-extra": "^11.0.0", + "get-stdin": "^9.0.0", + "globby": "^14.0.0", + "picocolors": "^1.0.0", + "postcss-load-config": "^5.0.0", + "postcss-reporter": "^7.0.0", + "pretty-hrtime": "^1.0.3", + "read-cache": "^1.0.0", + "slash": "^5.0.0", + "yargs": "^17.0.0" + }, + "bin": { + "postcss": "index.js" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-cli/node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/postcss-colormin": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-7.0.0.tgz", + "integrity": "sha512-5CN6fqtsEtEtwf3mFV3B4UaZnlYljPpzmGeDB4yCK067PnAtfLe9uX2aFZaEwxHE7HopG5rUkW8gyHrNAesHEg==", + "dev": true, + "dependencies": { + "browserslist": "^4.23.0", + "caniuse-api": "^3.0.0", + "colord": "^2.9.3", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-convert-values": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-7.0.0.tgz", + "integrity": "sha512-bMuzDgXBbFbByPgj+/r6va8zNuIDUaIIbvAFgdO1t3zdgJZ77BZvu6dfWyd6gHEJnYzmeVr9ayUsAQL3/qLJ0w==", + "dev": true, + "dependencies": { + "browserslist": "^4.23.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-discard-comments": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-7.0.0.tgz", + "integrity": "sha512-xpSdzRqYmy4YIVmjfGyYXKaI1SRnK6CTr+4Zmvyof8ANwvgfZgGdVtmgAvzh59gJm808mJCWQC9tFN0KF5dEXA==", + "dev": true, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-discard-duplicates": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-7.0.0.tgz", + "integrity": "sha512-bAnSuBop5LpAIUmmOSsuvtKAAKREB6BBIYStWUTGq8oG5q9fClDMMuY8i4UPI/cEcDx2TN+7PMnXYIId20UVDw==", + "dev": true, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-discard-empty": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-7.0.0.tgz", + "integrity": "sha512-e+QzoReTZ8IAwhnSdp/++7gBZ/F+nBq9y6PomfwORfP7q9nBpK5AMP64kOt0bA+lShBFbBDcgpJ3X4etHg4lzA==", + "dev": true, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-discard-overridden": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-7.0.0.tgz", + "integrity": "sha512-GmNAzx88u3k2+sBTZrJSDauR0ccpE24omTQCVmaTTZFz1du6AasspjaUPMJ2ud4RslZpoFKyf+6MSPETLojc6w==", + "dev": true, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-inline-svg": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-inline-svg/-/postcss-inline-svg-6.0.0.tgz", + "integrity": "sha512-ok5j0Iqsn8mS/5U1W+Im6qkQjm6nBxdwwJU+BSnBaDhLjC06h1xvy9MA+tefxhfZP/ARTRwARSozzYGf/sqEGg==", + "dev": true, + "dependencies": { + "css-select": "^5.1.0", + "dom-serializer": "^2.0.0", + "htmlparser2": "^8.0.1", + "postcss-value-parser": "^4.2.0" + }, + "peerDependencies": { + "postcss": "^8.1.4" + } + }, + "node_modules/postcss-inline-svg/node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, + "node_modules/postcss-load-config": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-5.0.3.tgz", + "integrity": "sha512-90pBBI5apUVruIEdCxZic93Wm+i9fTrp7TXbgdUCH+/L+2WnfpITSpq5dFU/IPvbv7aNiMlQISpUkAm3fEcvgQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + } + } + }, + "node_modules/postcss-merge-longhand": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-7.0.0.tgz", + "integrity": "sha512-0X8I4/9+G03X5/5NnrfopG/YEln2XU8heDh7YqBaiq2SeaKIG3n66ShZPjIolmVuLBQ0BEm3yS8o1mlCLHdW7A==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0", + "stylehacks": "^7.0.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-merge-rules": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-7.0.0.tgz", + "integrity": "sha512-Zty3VlOsD6VSjBMu6PiHCVpLegtBT/qtZRVBcSeyEZ6q1iU5qTYT0WtEoLRV+YubZZguS5/ycfP+NRiKfjv6aw==", + "dev": true, + "dependencies": { + "browserslist": "^4.23.0", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.16" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-minify-font-values": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-7.0.0.tgz", + "integrity": "sha512-2ckkZtgT0zG8SMc5aoNwtm5234eUx1GGFJKf2b1bSp8UflqaeFzR50lid4PfqVI9NtGqJ2J4Y7fwvnP/u1cQog==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-minify-gradients": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-7.0.0.tgz", + "integrity": "sha512-pdUIIdj/C93ryCHew0UgBnL2DtUS3hfFa5XtERrs4x+hmpMYGhbzo6l/Ir5de41O0GaKVpK1ZbDNXSY6GkXvtg==", + "dev": true, + "dependencies": { + "colord": "^2.9.3", + "cssnano-utils": "^5.0.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-minify-params": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-7.0.0.tgz", + "integrity": "sha512-XOJAuX8Q/9GT1sGxlUvaFEe2H9n50bniLZblXXsAT/BwSfFYvzSZeFG7uupwc0KbKpTnflnQ7aMwGzX6JUWliQ==", + "dev": true, + "dependencies": { + "browserslist": "^4.23.0", + "cssnano-utils": "^5.0.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-minify-selectors": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-7.0.0.tgz", + "integrity": "sha512-f00CExZhD6lNw2vTZbcnmfxVgaVKzUw6IRsIFX3JTT8GdsoABc1WnhhGwL1i8YPJ3sSWw39fv7XPtvLb+3Uitw==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.16" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-charset": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-7.0.0.tgz", + "integrity": "sha512-ABisNUXMeZeDNzCQxPxBCkXexvBrUHV+p7/BXOY+ulxkcjUZO0cp8ekGBwvIh2LbCwnWbyMPNJVtBSdyhM2zYQ==", + "dev": true, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-display-values": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-7.0.0.tgz", + "integrity": "sha512-lnFZzNPeDf5uGMPYgGOw7v0BfB45+irSRz9gHQStdkkhiM0gTfvWkWB5BMxpn0OqgOQuZG/mRlZyJxp0EImr2Q==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-positions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-7.0.0.tgz", + "integrity": "sha512-I0yt8wX529UKIGs2y/9Ybs2CelSvItfmvg/DBIjTnoUSrPxSV7Z0yZ8ShSVtKNaV/wAY+m7bgtyVQLhB00A1NQ==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-repeat-style": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-7.0.0.tgz", + "integrity": "sha512-o3uSGYH+2q30ieM3ppu9GTjSXIzOrRdCUn8UOMGNw7Af61bmurHTWI87hRybrP6xDHvOe5WlAj3XzN6vEO8jLw==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-string": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-7.0.0.tgz", + "integrity": "sha512-w/qzL212DFVOpMy3UGyxrND+Kb0fvCiBBujiaONIihq7VvtC7bswjWgKQU/w4VcRyDD8gpfqUiBQ4DUOwEJ6Qg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-timing-functions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-7.0.0.tgz", + "integrity": "sha512-tNgw3YV0LYoRwg43N3lTe3AEWZ66W7Dh7lVEpJbHoKOuHc1sLrzMLMFjP8SNULHaykzsonUEDbKedv8C+7ej6g==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-unicode": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-7.0.0.tgz", + "integrity": "sha512-OnKV52/VFFDAim4n0pdI+JAhsolLBdnCKxE6VV5lW5Q/JeVGFN8UM8ur6/A3EAMLsT1ZRm3fDHh/rBoBQpqi2w==", + "dev": true, + "dependencies": { + "browserslist": "^4.23.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-url": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-7.0.0.tgz", + "integrity": "sha512-+d7+PpE+jyPX1hDQZYG+NaFD+Nd2ris6r8fPTBAjE8z/U41n/bib3vze8x7rKs5H1uEw5ppe9IojewouHk0klQ==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-whitespace": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-7.0.0.tgz", + "integrity": "sha512-37/toN4wwZErqohedXYqWgvcHUGlT8O/m2jVkAfAe9Bd4MzRqlBmXrJRePH0e9Wgnz2X7KymTgTOaaFizQe3AQ==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-ordered-values": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-7.0.0.tgz", + "integrity": "sha512-KROvC63A8UQW1eYDljQe1dtwc1E/M+mMwDT6z7khV/weHYLWTghaLRLunU7x1xw85lWFwVZOAGakxekYvKV+0w==", + "dev": true, + "dependencies": { + "cssnano-utils": "^5.0.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-reduce-initial": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-7.0.0.tgz", + "integrity": "sha512-iqGgmBxY9LrblZ0BKLjmrA1mC/cf9A/wYCCqSmD6tMi+xAyVl0+DfixZIHSVDMbCPRPjNmVF0DFGth/IDGelFQ==", + "dev": true, + "dependencies": { + "browserslist": "^4.23.0", + "caniuse-api": "^3.0.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-reduce-transforms": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-7.0.0.tgz", + "integrity": "sha512-pnt1HKKZ07/idH8cpATX/ujMbtOGhUfE+m8gbqwJE05aTaNw8gbo34a2e3if0xc0dlu75sUOiqvwCGY3fzOHew==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-reporter": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/postcss-reporter/-/postcss-reporter-7.1.0.tgz", + "integrity": "sha512-/eoEylGWyy6/DOiMP5lmFRdmDKThqgn7D6hP2dXKJI/0rJSO1ADFNngZfDzxL0YAxFvws+Rtpuji1YIHj4mySA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "picocolors": "^1.0.0", + "thenby": "^1.3.4" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-resolve-nested-selector": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz", + "integrity": "sha512-HvExULSwLqHLgUy1rl3ANIqCsvMS0WHss2UOsXhXnQaZ9VCc2oBvIpXrl00IUFT5ZDITME0o6oiXeiHr2SAIfw==", + "dev": true + }, + "node_modules/postcss-safe-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.0.tgz", + "integrity": "sha512-ovehqRNVCpuFzbXoTb4qLtyzK3xn3t/CUBxOs8LsnQjQrShaB4lKiHoVqY8ANaC0hBMHq5QVWk77rwGklFUDrg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss-safe-parser" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "engines": { + "node": ">=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.16", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz", + "integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-svgo": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-7.0.0.tgz", + "integrity": "sha512-Xj5DRdvA97yRy3wjbCH2NKXtDUwEnph6EHr5ZXszsBVKCNrKXYBjzAXqav7/Afz5WwJ/1peZoTguCEJIg7ytmA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0", + "svgo": "^3.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >= 18" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-unique-selectors": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-7.0.0.tgz", + "integrity": "sha512-NYFqcft7vVQMZlQPsMdMPy+qU/zDpy95Malpw4GeA9ZZjM6dVXDshXtDmLc0m4WCD6XeZCJqjTfPT1USsdt+rA==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.16" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/potpack": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/potpack/-/potpack-2.0.0.tgz", + "integrity": "sha512-Q+/tYsFU9r7xoOJ+y/ZTtdVQwTWfzjbiXBDMM/JKUux3+QPP02iUuIoeBQ+Ot6oEDlC+/PGjB/5A3K7KKb7hcw==" + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/pretty-bytes": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-6.1.1.tgz", + "integrity": "sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==", + "dev": true, + "engines": { + "node": "^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/pretty-hrtime": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "integrity": "sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/printf": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/printf/-/printf-0.6.1.tgz", + "integrity": "sha512-is0ctgGdPJ5951KulgfzvHGwJtZ5ck8l042vRkV6jrkpBzTmb/lueTqguWHy2JfVA+RY6gFVlaZgUS0j7S/dsw==", + "dev": true, + "engines": { + "node": ">= 0.9.0" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/protocol-buffers-schema": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", + "integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-agent": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz", + "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==", + "dev": true, + "dependencies": { + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.1", + "https-proxy-agent": "^7.0.3", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.0.1", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true + }, + "node_modules/pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", + "dev": true + }, + "node_modules/public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/public-encrypt/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", + "dev": true + }, + "node_modules/puppeteer-core": { + "version": "22.8.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-22.8.0.tgz", + "integrity": "sha512-S5bWx3g/fNuyFxjZX9TkZMN07CEH47+9Zm6IiTl1QfqI9pnVaShbwrD9kRe5vmz/XPp/jLGhhxRUj1sY4wObnA==", + "dev": true, + "dependencies": { + "@puppeteer/browsers": "2.2.3", + "chromium-bidi": "0.5.19", + "debug": "4.3.4", + "devtools-protocol": "0.0.1273771", + "ws": "8.17.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/qrcode-terminal": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/qrcode-terminal/-/qrcode-terminal-0.12.0.tgz", + "integrity": "sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ==", + "dev": true, + "bin": { + "qrcode-terminal": "bin/qrcode-terminal.js" + } + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/queue-tick": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", + "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", + "dev": true + }, + "node_modules/quickselect": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", + "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==" + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "dependencies": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/read-only-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz", + "integrity": "sha512-3ALe0bjBVZtkdWKIcThYpQCLbBMd/+Tbh2CDSrAIDO3UsZ4Xs+tnyjv2MjCOMMgBG+AsUOeuP1cgtY1INISc8w==", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "dev": true, + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg/node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/readable-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", + "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-protobuf-schema": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", + "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==", + "dependencies": { + "protocol-buffers-schema": "^3.3.1" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/rollup": { + "version": "3.29.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", + "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=14.18.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/rollup-plugin-unassert": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-unassert/-/rollup-plugin-unassert-0.6.0.tgz", + "integrity": "sha512-wdfjsa8pPYFKRwHEOh2K9MAjkdEB864a8bTgDykqle85nGowY1lI8oCU5aqWG8zTc/bkF8/15C8ApzeBtT6VaA==", + "dev": true, + "dependencies": { + "@javascript-obfuscator/escodegen": "^2.3.0", + "@rollup/pluginutils": "^4.2.1", + "acorn": "^8.8.0", + "convert-source-map": "^1.8.0", + "multi-stage-sourcemap": "^0.3.1", + "unassert": "^2.0.0" + } + }, + "node_modules/rollup-plugin-unassert/node_modules/@rollup/pluginutils": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz", + "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==", + "dev": true, + "dependencies": { + "estree-walker": "^2.0.1", + "picomatch": "^2.2.2" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/rollup-plugin-unassert/node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/rollup-plugin-unassert/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "node_modules/run-async": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", + "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" + }, + "node_modules/safe-array-concat": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", + "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-array-concat/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-regex-test": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", + "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-regex": "^1.1.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "node_modules/seedrandom": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-2.4.4.tgz", + "integrity": "sha512-9A+PDmgm+2du77B5i0Ip2cxOqqHjgNxnBgglxLcX78A2D6c2rTo61z4jnVABpF4cKeDMDG+cmXXvdnqse2VqMA==", + "dev": true + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serialize-to-js": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/serialize-to-js/-/serialize-to-js-3.1.2.tgz", + "integrity": "sha512-owllqNuDDEimQat7EPG0tH7JjO090xKNzUtYz6X+Sk2BXDnOCilDdNLwjWeFywG9xkJul1ULvtUQa9O4pUaY0w==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dev": true, + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/shasum-object": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shasum-object/-/shasum-object-1.0.0.tgz", + "integrity": "sha512-Iqo5rp/3xVi6M4YheapzZhhGPVs0yZwHj7wvwQ1B9z8H6zk+FEnI7y3Teq7qwnekfEhu8WmG2z0z4iWZaxLWVg==", + "dev": true, + "dependencies": { + "fast-safe-stringify": "^2.0.7" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/shellwords": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", + "dev": true + }, + "node_modules/shuffle-seed": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/shuffle-seed/-/shuffle-seed-1.1.6.tgz", + "integrity": "sha512-Vr9wlwMKJVUeFNGyc4aNbrzkI568gkve7ykyJ+1/cz78j3yRlJODWU0CuJ/fmk3MCjvAClpnqlycd/Y53UG3UA==", + "dev": true, + "dependencies": { + "seedrandom": "^2.4.2" + } + }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/sirv": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", + "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==", + "dev": true, + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/slice-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/smob": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/smob/-/smob-1.4.1.tgz", + "integrity": "sha512-9LK+E7Hv5R9u4g4C3p+jjLstaLe11MDsL21UpYaCNmapvMkYhqCV4A/f/3gyH8QjMyh6l68q9xC85vihY9ahMQ==", + "dev": true + }, + "node_modules/socket.io": { + "version": "4.7.5", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.5.tgz", + "integrity": "sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA==", + "dev": true, + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.5.2", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.4.tgz", + "integrity": "sha512-wDNHGXGewWAjQPt3pyeYBtpWSq9cLE5UW1ZUPL/2eGK9jtse/FpXib7epSTsz0Q0m+6sg6Y4KtcFTlah1bdOVg==", + "dev": true, + "dependencies": { + "debug": "~4.3.4", + "ws": "~8.11.0" + } + }, + "node_modules/socket.io-adapter/node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dev": true, + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", + "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", + "dev": true, + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.3.tgz", + "integrity": "sha512-VNegTZKhuGq5vSD6XNKlbqWhyt/40CgoEw8XxD6dhnm8Jq9IEa3nIa4HwnM8XOqU0CdB0BwWVXusqiFXfHB3+A==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.1", + "debug": "^4.3.4", + "socks": "^2.7.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spawn-args": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/spawn-args/-/spawn-args-0.2.0.tgz", + "integrity": "sha512-73BoniQDcRWgnLAf/suKH6V5H54gd1KLzwYN9FB6J/evqTV33htH9xwV/4BHek+++jzxpVlZQKKZkqstPQPmQg==", + "dev": true + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-correct/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-4.0.0.tgz", + "integrity": "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz", + "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==", + "dev": true + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/st": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/st/-/st-3.0.0.tgz", + "integrity": "sha512-UEUi8P8Y5GOewlJbE5vrhsaQRwmbNVMUr6PLxRZHH4Cwz8CkHhnBqlqGtE3egXQd+ceUwNxdOVjsC/IsgN2Pww==", + "dev": true, + "dependencies": { + "async-cache": "^1.1.0", + "bl": "^5.0.0", + "fd": "~0.0.3", + "mime": "^2.5.2", + "negotiator": "~0.6.2" + }, + "bin": { + "st": "bin/server.js" + }, + "optionalDependencies": { + "graceful-fs": "^4.2.3" + } + }, + "node_modules/st/node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/std-env": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz", + "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==", + "dev": true + }, + "node_modules/stop-iteration-iterator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", + "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", + "dev": true, + "dependencies": { + "internal-slot": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/stream-browserify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", + "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", + "dev": true, + "dependencies": { + "inherits": "~2.0.4", + "readable-stream": "^3.5.0" + } + }, + "node_modules/stream-browserify/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/stream-combiner2": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", + "integrity": "sha512-3PnJbYgS56AeWgtKF5jtJRT6uFJe56Z0Hc5Ngg/6sI6rIt8iiMBTa9cvdyFfpMQjaVHr8dusbNeFGIIonxOvKw==", + "dev": true, + "dependencies": { + "duplexer2": "~0.1.0", + "readable-stream": "^2.0.2" + } + }, + "node_modules/stream-http": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.2.0.tgz", + "integrity": "sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==", + "dev": true, + "dependencies": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "xtend": "^4.0.2" + } + }, + "node_modules/stream-http/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/stream-splicer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/stream-splicer/-/stream-splicer-2.0.1.tgz", + "integrity": "sha512-Xizh4/NPuYSyAXyT7g8IvdJ9HJpxIGL9PjyhtywCZvvP0OPIdqyrr4dMikeuvY8xahpdKEBlBTySe583totajg==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.2" + } + }, + "node_modules/streamx": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.16.1.tgz", + "integrity": "sha512-m9QYj6WygWyWa3H1YY69amr4nVgy61xfjys7xO7kviL5rfIEc2naf+ewFiOA+aEJD7y0JO3h2GoiUv4TDwEGzQ==", + "dev": true, + "dependencies": { + "fast-fifo": "^1.1.0", + "queue-tick": "^1.0.1" + }, + "optionalDependencies": { + "bare-events": "^2.2.0" + } + }, + "node_modules/strict-event-emitter": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz", + "integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==", + "dev": true + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-natural-compare": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/string-natural-compare/-/string-natural-compare-3.0.1.tgz", + "integrity": "sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==", + "dev": true + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/string.prototype.padend": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.6.tgz", + "integrity": "sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", + "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", + "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.1.0.tgz", + "integrity": "sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==", + "dev": true, + "dependencies": { + "js-tokens": "^9.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/strip-literal/node_modules/js-tokens": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.0.tgz", + "integrity": "sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==", + "dev": true + }, + "node_modules/styled_string": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/styled_string/-/styled_string-0.0.1.tgz", + "integrity": "sha512-DU2KZiB6VbPkO2tGSqQ9n96ZstUPjW7X4sGO6V2m1myIQluX0p1Ol8BrA/l6/EesqhMqXOIXs3cJNOy1UuU2BA==", + "dev": true + }, + "node_modules/stylehacks": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-7.0.0.tgz", + "integrity": "sha512-47Nw4pQ6QJb4CA6dzF2m9810sjQik4dfk4UwAm5wlwhrW3syzZKF8AR4/cfO3Cr6lsFgAoznQq0Wg57qhjTA2A==", + "dev": true, + "dependencies": { + "browserslist": "^4.23.0", + "postcss-selector-parser": "^6.0.16" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/stylelint": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.5.0.tgz", + "integrity": "sha512-IlCBtVrG+qTy3v+tZTk50W8BIomjY/RUuzdrDqdnlCYwVuzXtPbiGfxYqtyYAyOMcb+195zRsuHn6tgfPmFfbw==", + "dev": true, + "dependencies": { + "@csstools/css-parser-algorithms": "^2.6.1", + "@csstools/css-tokenizer": "^2.2.4", + "@csstools/media-query-list-parser": "^2.1.9", + "@csstools/selector-specificity": "^3.0.3", + "@dual-bundle/import-meta-resolve": "^4.0.0", + "balanced-match": "^2.0.0", + "colord": "^2.9.3", + "cosmiconfig": "^9.0.0", + "css-functions-list": "^3.2.2", + "css-tree": "^2.3.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "fastest-levenshtein": "^1.0.16", + "file-entry-cache": "^8.0.0", + "global-modules": "^2.0.0", + "globby": "^11.1.0", + "globjoin": "^0.1.4", + "html-tags": "^3.3.1", + "ignore": "^5.3.1", + "imurmurhash": "^0.1.4", + "is-plain-object": "^5.0.0", + "known-css-properties": "^0.30.0", + "mathml-tag-names": "^2.1.3", + "meow": "^13.2.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.38", + "postcss-resolve-nested-selector": "^0.1.1", + "postcss-safe-parser": "^7.0.0", + "postcss-selector-parser": "^6.0.16", + "postcss-value-parser": "^4.2.0", + "resolve-from": "^5.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^7.1.0", + "supports-hyperlinks": "^3.0.0", + "svg-tags": "^1.0.0", + "table": "^6.8.2", + "write-file-atomic": "^5.0.1" + }, + "bin": { + "stylelint": "bin/stylelint.mjs" + }, + "engines": { + "node": ">=18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + } + }, + "node_modules/stylelint-config-recommended": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-14.0.0.tgz", + "integrity": "sha512-jSkx290CglS8StmrLp2TxAppIajzIBZKYm3IxT89Kg6fGlxbPiTiyH9PS5YUuVAFwaJLl1ikiXX0QWjI0jmgZQ==", + "dev": true, + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "stylelint": "^16.0.0" + } + }, + "node_modules/stylelint-config-standard": { + "version": "36.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-36.0.0.tgz", + "integrity": "sha512-3Kjyq4d62bYFp/Aq8PMKDwlgUyPU4nacXsjDLWJdNPRUgpuxALu1KnlAHIj36cdtxViVhXexZij65yM0uNIHug==", + "dev": true, + "dependencies": { + "stylelint-config-recommended": "^14.0.0" + }, + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "stylelint": "^16.1.0" + } + }, + "node_modules/stylelint/node_modules/balanced-match": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz", + "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", + "dev": true + }, + "node_modules/stylelint/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/stylelint/node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/stylelint/node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/stylelint/node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stylelint/node_modules/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/stylelint/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/stylelint/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/stylelint/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/stylelint/node_modules/string-width/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/stylelint/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/stylelint/node_modules/strip-ansi/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/subarg": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", + "integrity": "sha512-RIrIdRY0X1xojthNcVtgT9sjpOGagEUKpZdgBUi054OEPFo282yg+zE+t1Rj3+RqKq2xStL7uUHhY+AjbC4BXg==", + "dev": true, + "dependencies": { + "minimist": "^1.1.0" + } + }, + "node_modules/supercluster": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-8.0.1.tgz", + "integrity": "sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ==", + "dependencies": { + "kdbush": "^4.0.2" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.0.0.tgz", + "integrity": "sha512-QBDPHyPQDRTy9ku4URNGY5Lah8PAaXs6tAAwp55sL5WCsSW7GIfdf6W5ixfziW+t7wh3GVvHyHHyQ1ESsoRvaA==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=14.18" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svg-tags": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", + "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==", + "dev": true + }, + "node_modules/svgo": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.2.0.tgz", + "integrity": "sha512-4PP6CMW/V7l/GmKRKzsLR8xxjdHTV4IMvhTnpuHwwBazSIlw5W/5SmPjN8Dwyt7lKbSJrRDgp4t9ph0HgChFBQ==", + "dev": true, + "dependencies": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^5.1.0", + "css-tree": "^2.3.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.0.0" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" + } + }, + "node_modules/syntax-error": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.4.0.tgz", + "integrity": "sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w==", + "dev": true, + "dependencies": { + "acorn-node": "^1.2.0" + } + }, + "node_modules/table": { + "version": "6.8.2", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.2.tgz", + "integrity": "sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/table/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/table/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tap-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/tap-parser/-/tap-parser-7.0.0.tgz", + "integrity": "sha512-05G8/LrzqOOFvZhhAk32wsGiPZ1lfUrl+iV7+OkKgfofZxiceZWMHkKmow71YsyVQ8IvGBP2EjcIjE5gL4l5lA==", + "dev": true, + "dependencies": { + "events-to-array": "^1.0.1", + "js-yaml": "^3.2.7", + "minipass": "^2.2.0" + }, + "bin": { + "tap-parser": "bin/cmd.js" + } + }, + "node_modules/tap-parser/node_modules/minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "node_modules/tape": { + "version": "5.7.5", + "resolved": "https://registry.npmjs.org/tape/-/tape-5.7.5.tgz", + "integrity": "sha512-C5Gm1MR8ujZmNrsmOiHSkKFfY2thrnUrFw/fFtcva9FABbN7LrHuQPi3MTS0Z0i/SLfYSJtRIcJYDUpwPsQ8yA==", + "dev": true, + "dependencies": { + "@ljharb/resumer": "^0.1.2", + "@ljharb/through": "^2.3.12", + "array.prototype.every": "^1.1.5", + "call-bind": "^1.0.7", + "deep-equal": "^2.2.3", + "defined": "^1.0.1", + "dotignore": "^0.1.2", + "for-each": "^0.3.3", + "get-package-type": "^0.1.0", + "glob": "^7.2.3", + "has-dynamic-import": "^2.1.0", + "hasown": "^2.0.1", + "inherits": "^2.0.4", + "is-regex": "^1.1.4", + "minimist": "^1.2.8", + "mock-property": "^1.0.3", + "object-inspect": "^1.13.1", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "object.assign": "^4.1.5", + "resolve": "^2.0.0-next.5", + "string.prototype.trim": "^1.2.8" + }, + "bin": { + "tape": "bin/tape" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tape-filter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tape-filter/-/tape-filter-1.0.4.tgz", + "integrity": "sha512-iLXfCT4yxphhbYQNqpYpoyB5LJpem6QoRZKfqTuzI/qJUwzlyZSA6+V22Abnk1RoKB6ovuWBErwxLq4mgTQOFg==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + } + }, + "node_modules/tape/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/tape/node_modules/resolve": { + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tar-fs": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.5.tgz", + "integrity": "sha512-JOgGAmZyMgbqpLwct7ZV8VzkEB6pxXFBVErLtb+XCOqzc6w1xiWKI9GVd6bwk68EX7eJ4DWmfXVmq8K2ziZTGg==", + "dev": true, + "dependencies": { + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^2.1.1", + "bare-path": "^2.1.0" + } + }, + "node_modules/tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "dev": true, + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/terser": { + "version": "5.29.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.29.2.tgz", + "integrity": "sha512-ZiGkhUBIM+7LwkNjXYJq8svgkd+QK3UUr0wJqY4MieaezBSAIPgbSPZyIx0idM6XWK5CMzSWa8MJIzmRcB8Caw==", + "dev": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser/node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/testem": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/testem/-/testem-3.13.0.tgz", + "integrity": "sha512-b4hdlkH2TR1TQJCOgBNbD7nz4TjeYF35MgUlzum3yfDaaR+lJDjmJNMgi72MKgg+SjkGZ1U3BCBOqLC85MsMmQ==", + "dev": true, + "dependencies": { + "@xmldom/xmldom": "^0.8.0", + "backbone": "^1.1.2", + "bluebird": "^3.4.6", + "charm": "^1.0.0", + "commander": "^2.6.0", + "compression": "^1.7.4", + "consolidate": "^0.16.0", + "execa": "^1.0.0", + "express": "^4.10.7", + "fireworm": "^0.7.0", + "glob": "^7.0.4", + "http-proxy": "^1.13.1", + "js-yaml": "^3.2.5", + "lodash.assignin": "^4.1.0", + "lodash.castarray": "^4.4.0", + "lodash.clonedeep": "^4.4.1", + "lodash.find": "^4.5.1", + "lodash.uniqby": "^4.7.0", + "mkdirp": "^3.0.1", + "mustache": "^4.2.0", + "node-notifier": "^10.0.0", + "npmlog": "^6.0.0", + "printf": "^0.6.1", + "rimraf": "^3.0.2", + "socket.io": "^4.5.4", + "spawn-args": "^0.2.0", + "styled_string": "0.0.1", + "tap-parser": "^7.0.0", + "tmp": "0.0.33" + }, + "bin": { + "testem": "testem.js" + }, + "engines": { + "node": ">= 7.*" + } + }, + "node_modules/testem/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/testem/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/thenby": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/thenby/-/thenby-1.3.4.tgz", + "integrity": "sha512-89Gi5raiWA3QZ4b2ePcEwswC3me9JIg+ToSgtE0JWeCynLnLxNr/f9G+xfo9K+Oj4AFdom8YNJjibIARTJmapQ==", + "dev": true + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/timers-browserify": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz", + "integrity": "sha512-PIxwAupJZiYU4JmVZYwXp9FKsHMXb5h0ZEFyuXTAn8WLHOlcij+FEcbrvDsom1o5dr1YggEtFbECvGCW2sT53Q==", + "dev": true, + "dependencies": { + "process": "~0.11.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/tiny-lru": { + "version": "11.2.6", + "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-11.2.6.tgz", + "integrity": "sha512-0PU3c9PjMnltZaFo2sGYv/nnJsMjG0Cxx8X6FXHPPGjFyoo1SJDxvUXW1207rdiSxYizf31roo+GrkIByQeZoA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/tinybench": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.8.0.tgz", + "integrity": "sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==", + "dev": true + }, + "node_modules/tinypool": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.4.tgz", + "integrity": "sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyqueue": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz", + "integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==" + }, + "node_modules/tinyspy": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz", + "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, + "node_modules/tty-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", + "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==", + "dev": true + }, + "node_modules/tweakpane": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/tweakpane/-/tweakpane-4.0.3.tgz", + "integrity": "sha512-BlcWOAe8oe4c+k9pmLBARGdWB6MVZMszayekkixQXTgkxTaYoTUpHpwVEp+3HkoamZkomodpbBf0CkguIHTgLg==", + "funding": { + "url": "https://github.com/sponsors/cocopon" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.13.1.tgz", + "integrity": "sha512-ASMgM+Vf2cLwDMt1KXSkMUDSYCxtckDJs8zsaVF/mYteIsiARKCVtyXtcK38mIKbLTctZP8v6GMqdNaeI3fo7g==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", + "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", + "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", + "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "dev": true + }, + "node_modules/ufo": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.3.tgz", + "integrity": "sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==", + "dev": true + }, + "node_modules/umd": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.3.tgz", + "integrity": "sha512-4IcGSufhFshvLNcMCV80UnQVlZ5pMOC8mvNPForqwA4+lzYQuetTESLDQkeLmihq8bRcnpbQa48Wb8Lh16/xow==", + "dev": true, + "bin": { + "umd": "bin/cli.js" + } + }, + "node_modules/unassert": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unassert/-/unassert-2.0.2.tgz", + "integrity": "sha512-P6OOg/aRdQmWH+b0g+T4U+9MgL+DG7w6oQPG+N3F2IMuvvd1WfZ5alT/Rjik2lMFVyhfACUxF7PGP1VCwSHlQA==", + "dev": true, + "dependencies": { + "estraverse": "^5.0.0" + } + }, + "node_modules/unassert/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/unbzip2-stream": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", + "dev": true, + "dependencies": { + "buffer": "^5.2.1", + "through": "^2.3.8" + } + }, + "node_modules/undeclared-identifiers": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/undeclared-identifiers/-/undeclared-identifiers-1.1.3.tgz", + "integrity": "sha512-pJOW4nxjlmfwKApE4zvxLScM/njmwj/DiUBv7EabwE4O8kRUy+HIwxQtZLBPll/jx1LJyBcqNfB3/cpv9EZwOw==", + "dev": true, + "dependencies": { + "acorn-node": "^1.3.0", + "dash-ast": "^1.0.0", + "get-assigned-identifiers": "^1.2.0", + "simple-concat": "^1.0.0", + "xtend": "^4.0.1" + }, + "bin": { + "undeclared-identifiers": "bin.js" + } + }, + "node_modules/underscore": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", + "dev": true + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/universal-user-agent": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz", + "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==", + "dev": true + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/uri-js/node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/url": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.3.tgz", + "integrity": "sha512-6hxOLGfZASQK/cijlZnZJTq8OXAkt/3YGfQX45vvMYXpZoo8NdWZcY73K108Jf759lS1Bv/8wXnHDTSz17dSRw==", + "dev": true, + "dependencies": { + "punycode": "^1.4.1", + "qs": "^6.11.2" + } + }, + "node_modules/url/node_modules/qs": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.0.tgz", + "integrity": "sha512-trVZiI6RMOkO476zLGaBIzszOdFPnCCXHPG9kn0yuS1uz6xdVxPfZdB3vUig9pxPFDM9BRAgz/YUIVQ1/vuiUg==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/urlpattern-polyfill": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", + "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==", + "dev": true + }, + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz", + "integrity": "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==", + "dev": true + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/validate-npm-package-license/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vite": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.3.tgz", + "integrity": "sha512-+i1oagbvkVIhEy9TnEV+fgXsng13nZM90JQbrcPrf6DvW2mXARlz+DK7DLiDP+qeKoD1FCVx/1SpFL1CLq9Mhw==", + "dev": true, + "dependencies": { + "esbuild": "^0.20.1", + "postcss": "^8.4.36", + "rollup": "^4.13.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.6.0.tgz", + "integrity": "sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==", + "dev": true, + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.4", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite-plugin-arraybuffer": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/vite-plugin-arraybuffer/-/vite-plugin-arraybuffer-0.0.7.tgz", + "integrity": "sha512-c4Egxj7NUGco2Ggw9KUBToOxuc7Ws7mWm0hz/QnaT5Ph8ycC7ypMBOD31NuhPSx+wdUvgIbS1XpMvJLSdHakPA==", + "dev": true + }, + "node_modules/vite-plugin-node-polyfills": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/vite-plugin-node-polyfills/-/vite-plugin-node-polyfills-0.21.0.tgz", + "integrity": "sha512-Sk4DiKnmxN8E0vhgEhzLudfJQfaT8k4/gJ25xvUPG54KjLJ6HAmDKbr4rzDD/QWEY+Lwg80KE85fGYBQihEPQA==", + "dev": true, + "dependencies": { + "@rollup/plugin-inject": "^5.0.5", + "node-stdlib-browser": "^1.2.0" + }, + "funding": { + "url": "https://github.com/sponsors/davidmyersdev" + }, + "peerDependencies": { + "vite": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0" + } + }, + "node_modules/vite/node_modules/rollup": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.0.tgz", + "integrity": "sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==", + "dev": true, + "dependencies": { + "@types/estree": "1.0.5" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.13.0", + "@rollup/rollup-android-arm64": "4.13.0", + "@rollup/rollup-darwin-arm64": "4.13.0", + "@rollup/rollup-darwin-x64": "4.13.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.13.0", + "@rollup/rollup-linux-arm64-gnu": "4.13.0", + "@rollup/rollup-linux-arm64-musl": "4.13.0", + "@rollup/rollup-linux-riscv64-gnu": "4.13.0", + "@rollup/rollup-linux-x64-gnu": "4.13.0", + "@rollup/rollup-linux-x64-musl": "4.13.0", + "@rollup/rollup-win32-arm64-msvc": "4.13.0", + "@rollup/rollup-win32-ia32-msvc": "4.13.0", + "@rollup/rollup-win32-x64-msvc": "4.13.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/vitest": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.6.0.tgz", + "integrity": "sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==", + "dev": true, + "dependencies": { + "@vitest/expect": "1.6.0", + "@vitest/runner": "1.6.0", + "@vitest/snapshot": "1.6.0", + "@vitest/spy": "1.6.0", + "@vitest/utils": "1.6.0", + "acorn-walk": "^8.3.2", + "chai": "^4.3.10", + "debug": "^4.3.4", + "execa": "^8.0.1", + "local-pkg": "^0.5.0", + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "std-env": "^3.5.0", + "strip-literal": "^2.0.0", + "tinybench": "^2.5.1", + "tinypool": "^0.8.3", + "vite": "^5.0.0", + "vite-node": "1.6.0", + "why-is-node-running": "^2.2.2" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "1.6.0", + "@vitest/ui": "1.6.0", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/acorn-walk": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/vitest/node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/vitest/node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vitest/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vitest/node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vitest/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vlq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/vlq/-/vlq-1.0.1.tgz", + "integrity": "sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==", + "dev": true + }, + "node_modules/vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "dev": true + }, + "node_modules/vt-pbf": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/vt-pbf/-/vt-pbf-3.1.3.tgz", + "integrity": "sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA==", + "dependencies": { + "@mapbox/point-geometry": "0.1.0", + "@mapbox/vector-tile": "^1.3.1", + "pbf": "^3.2.1" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/why-is-node-running": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz", + "integrity": "sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==", + "dev": true, + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dev": true, + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wide-align/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wide-align/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/ws": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz", + "integrity": "sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yaml": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.1.tgz", + "integrity": "sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==", + "dev": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", + "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/package.json b/package.json index e7ea0b3088a..3b35440d555 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "mapbox-gl", "description": "A WebGL interactive maps library", - "version": "3.0.0", + "version": "3.4.0-beta.1", "main": "dist/mapbox-gl.js", "style": "dist/mapbox-gl.css", "license": "SEE LICENSE IN LICENSE.txt", @@ -11,9 +11,8 @@ "url": "git://github.com/mapbox/mapbox-gl-js.git" }, "dependencies": { - "@mapbox/geojson-rewind": "^0.5.2", "@mapbox/jsonlint-lines-primitives": "^2.0.2", - "@mapbox/mapbox-gl-supported": "^2.0.1", + "@mapbox/mapbox-gl-supported": "^3.0.0", "@mapbox/point-geometry": "^0.1.0", "@mapbox/tiny-sdf": "^2.0.6", "@mapbox/unitbezier": "^0.0.1", @@ -22,87 +21,89 @@ "cheap-ruler": "^3.0.1", "csscolorparser": "~1.0.3", "earcut": "^2.2.4", + "fflate": "^0.8.1", "geojson-vt": "^3.2.1", "gl-matrix": "^3.4.3", "grid-index": "^1.1.0", "kdbush": "^4.0.1", + "lodash.clonedeep": "^4.5.0", "murmurhash-js": "^1.0.0", "pbf": "^3.2.1", "potpack": "^2.0.0", "quickselect": "^2.0.0", "rw": "^1.3.3", + "serialize-to-js": "^3.1.2", "supercluster": "^8.0.0", + "tiny-lru": "^11.2.6", "tinyqueue": "^2.0.3", + "tweakpane": "^4.0.3", "vt-pbf": "^3.1.3" }, "devDependencies": { - "@babel/core": "^7.23.2", - "@babel/eslint-parser": "^7.22.15", + "@babel/core": "^7.24.5", + "@babel/eslint-parser": "^7.24.5", "@mapbox/flow-remove-types": "^2.0.0", "@mapbox/mvt-fixtures": "^3.10.0", - "@octokit/rest": "^20.0.2", + "@octokit/rest": "^20.1.1", + "@rollup/plugin-alias": "^5.1.0", "@rollup/plugin-commonjs": "^25.0.7", - "@rollup/plugin-json": "^6.0.1", + "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-replace": "^5.0.5", "@rollup/plugin-strip": "^3.0.4", "@rollup/plugin-terser": "^0.4.4", - "address": "^1.2.0", + "@vitest/browser": "^1.6.0", + "@vitest/ui": "^1.6.0", + "address": "^2.0.2", "browserify": "^17.0.0", "chalk": "^5.0.1", - "chokidar": "^3.5.3", + "chokidar": "^3.6.0", "cross-env": "^7.0.3", - "cssnano": "^6.0.0", + "cssnano": "^7.0.1", "d3-queue": "^3.0.7", - "diff": "^5.1.0", - "ejs": "^3.1.8", + "diff": "^5.2.0", + "ejs": "^3.1.10", "envify": "^4.1.0", "eslint": "^7.32.0", "eslint-config-mourner": "^3.0.0", "eslint-plugin-flowtype": "^7.0.0", - "eslint-plugin-html": "^7.1.0", - "eslint-plugin-import": "^2.28.1", - "eslint-plugin-jsdoc": "^46.8.2", + "eslint-plugin-html": "^8.1.1", + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-jsdoc": "^48.2.3", "flow-bin": "0.191.0", - "gl": "6.0.2", - "glob": "^10.3.10", - "is-builtin-module": "^3.2.1", - "jsdom": "^15.2.1", + "glob": "^10.3.12", + "is-builtin-module": "^4.0.0", + "jest-extended": "^4.0.2", "json-stringify-pretty-compact": "^4.0.0", "lodash.template": "^4.5.0", "mapbox-gl-styles": "^2.0.2", "minimist": "^1.2.6", "mock-geolocation": "^1.0.11", + "msw": "^2.2.11", "node-notifier": "^10.0.1", "npm-font-open-sans": "^1.1.0", "npm-run-all": "^4.1.5", - "nyc": "^15.1.0", "pixelmatch": "^5.3.0", - "postcss": "^8.4.31", - "postcss-cli": "^10.1.0", + "playwright": "^1.43.1", + "postcss": "^8.4.38", + "postcss-cli": "^11.0.0", "postcss-inline-svg": "^6.0.0", "pretty-bytes": "^6.0.0", - "puppeteer-core": "^21.5.0", + "puppeteer-core": "^22.8.0", "qrcode-terminal": "^0.12.0", "rollup": "3.29.4", - "rollup-plugin-sourcemaps": "^0.6.3", "rollup-plugin-unassert": "^0.6.0", - "selenium-webdriver": "^4.15.0", "serve-static": "^1.15.0", "shuffle-seed": "^1.1.6", - "sinon": "^17.0.1", "st": "^3.0.0", - "stylelint": "^15.11.0", - "stylelint-config-standard": "^34.0.0", - "tap": "~16.3.9", - "tape": "^5.7.2", + "stylelint": "^16.5.0", + "stylelint-config-standard": "^36.0.0", + "tape": "^5.7.5", "tape-filter": "^1.0.4", - "testem": "^3.11.0" - }, - "browser": { - "./src/shaders/index.js": "./src/shaders/shaders.js", - "./src/util/window.js": "./src/util/browser/window.js", - "./src/util/web_worker.js": "./src/util/browser/web_worker.js" + "testem": "^3.13.0", + "vite-plugin-arraybuffer": "^0.0.7", + "vite-plugin-node-polyfills": "^0.21.0", + "vitest": "^1.6.0" }, "scripts": { "build-dev": "rollup -c --environment BUILD:dev", @@ -127,11 +128,12 @@ "test": "run-s lint lint-css test-flow test-unit", "test-suite": "run-s test-render test-query test-expressions", "test-suite-clean": "find test/integration/{render,query, expressions}-tests -mindepth 2 -type d -exec test -e \"{}/actual.png\" \\; -not \\( -exec test -e \"{}/style.json\" \\; \\) -print | xargs -t rm -r", - "test-unit": "build/run-tap --reporter classic --no-coverage test/unit", - "test-build": "build/run-tap --no-coverage test/build/**/*.test.js", - "test-browser": "build/run-tap --jobs=1 --reporter spec --no-coverage -- test/browser/**/*.test.js", + "watch-unit": "vitest --config vitest.config.unit.js", + "test-unit": "vitest --config vitest.config.unit.js --run", + "test-build": "tape test/build/**/*.test.js", "watch-render": "cross-env SUITE_NAME=render testem -f test/integration/testem/testem.js", "watch-query": "SUITE_NAME=query testem -f test/integration/testem/testem.js", + "test-csp": "vitest --config vitest.config.csp.js --run", "test-render": "cross-env SUITE_NAME=render testem ci -f test/integration/testem/testem.js", "test-render-firefox": "cross-env BROWSER=Firefox SUITE_NAME=render testem ci -f test/integration/testem/testem.js", "test-render-safari": "cross-env BROWSER=Safari SUITE_NAME=render testem ci -f test/integration/testem/testem.js", @@ -140,7 +142,6 @@ "test-query": "SUITE_NAME=query testem ci -f test/integration/testem/testem.js", "test-expressions": "build/run-node test/expression.test.js", "test-flow": "build/run-node build/generate-flow-typed-style-spec && flow .", - "test-cov": "nyc --require=@mapbox/flow-remove-types/register --reporter=text-summary --reporter=lcov --cache run-s test-unit test-expressions test-query test-render", "test-style-spec": "cd src/style-spec && npm test", "prepublishOnly": "run-s build-flow-types build-dev build-prod-min build-prod build-csp build-css build-style-spec", "print-release-url": "node build/print-release-url.js", @@ -156,5 +157,10 @@ "src/", ".flowconfig", "LICENSE.txt" - ] + ], + "msw": { + "workerDirectory": [ + "test/util" + ] + } } diff --git a/rollup.config.js b/rollup.config.js index da6a290ecf4..8b338a43753 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,8 +1,10 @@ +/* eslint-disable flowtype/require-valid-file-annotation */ import fs from 'fs'; -import sourcemaps from 'rollup-plugin-sourcemaps'; +import {fileURLToPath} from 'url'; +import {readFile} from 'node:fs/promises'; + import {plugins} from './build/rollup_plugins.js'; import banner from './build/banner.js'; -import {fileURLToPath} from 'url'; const {BUILD, MINIFY} = process.env; const minified = MINIFY === 'true'; @@ -11,15 +13,15 @@ const production = BUILD === 'production' || bench; function buildType(build, minified) { switch (build) { - case 'production': - if (minified) return 'dist/mapbox-gl.js'; - return 'dist/mapbox-gl-unminified.js'; - case 'bench': - return 'dist/mapbox-gl-bench.js'; - case 'dev': - return 'dist/mapbox-gl-dev.js'; - default: - return 'dist/mapbox-gl-dev.js'; + case 'production': + if (minified) return 'dist/mapbox-gl.js'; + return 'dist/mapbox-gl-unminified.js'; + case 'bench': + return 'dist/mapbox-gl-bench.js'; + case 'dev': + return 'dist/mapbox-gl-dev.js'; + default: + return 'dist/mapbox-gl-dev.js'; } } const outputFile = buildType(BUILD, MINIFY); @@ -42,6 +44,7 @@ export default [{ chunkFileNames: 'shared.js', minifyInternalExports: production }, + onwarn: production ? onwarn : false, treeshake: production, plugins: plugins({minified, production, bench}) }, { @@ -65,3 +68,31 @@ export default [{ sourcemaps() ], }]; + +function sourcemaps() { + const base64SourceMapRegExp = /\/\/# sourceMappingURL=data:[^,]+,([^ ]+)/; + + return { + name: 'sourcemaps', + async load(id) { + const code = await readFile(id, {encoding: 'utf8'}); + const match = base64SourceMapRegExp.exec(code); + if (!match) return; + + const base64EncodedSourceMap = match[1]; + const decodedSourceMap = Buffer.from(base64EncodedSourceMap, 'base64').toString('utf-8'); + const map = JSON.parse(decodedSourceMap); + + return {code, map}; + } + }; +} + +function onwarn(warning) { + if (warning.code === 'CIRCULAR_DEPENDENCY') { + // Ignore circular dependencies in style-spec and throw on all others + if (!warning.ids[0].includes('/src/style-spec')) throw new Error(warning.message); + } else { + console.error(`(!) ${warning.message}`); + } +} diff --git a/src/data/array_types.js b/src/data/array_types.js index 3ff869d5e23..31fb2a296ee 100644 --- a/src/data/array_types.js +++ b/src/data/array_types.js @@ -1,10 +1,11 @@ -// This file is generated. Edit build/generate-struct-arrays.js, then run `yarn run codegen`. +// This file is generated. Edit build/generate-struct-arrays.js, then run `npm run codegen`. /* eslint-disable camelcase */ // @flow import assert from 'assert'; import {Struct, StructArray} from '../util/struct_array.js'; import {register} from '../util/web_worker_transfer.js'; +import type {IStructArrayLayout} from '../util/struct_array.js'; /** * Implementation of the StructArray layout: @@ -12,7 +13,7 @@ import {register} from '../util/web_worker_transfer.js'; * * @private */ -class StructArrayLayout2i4 extends StructArray { +class StructArrayLayout2i4 extends StructArray implements IStructArrayLayout { uint8: Uint8Array; int16: Int16Array; @@ -44,7 +45,7 @@ register(StructArrayLayout2i4, 'StructArrayLayout2i4'); * * @private */ -class StructArrayLayout3i6 extends StructArray { +class StructArrayLayout3i6 extends StructArray implements IStructArrayLayout { uint8: Uint8Array; int16: Int16Array; @@ -77,7 +78,7 @@ register(StructArrayLayout3i6, 'StructArrayLayout3i6'); * * @private */ -class StructArrayLayout4i8 extends StructArray { +class StructArrayLayout4i8 extends StructArray implements IStructArrayLayout { uint8: Uint8Array; int16: Int16Array; @@ -111,7 +112,7 @@ register(StructArrayLayout4i8, 'StructArrayLayout4i8'); * * @private */ -class StructArrayLayout5i10 extends StructArray { +class StructArrayLayout5i10 extends StructArray implements IStructArrayLayout { uint8: Uint8Array; int16: Int16Array; @@ -148,7 +149,7 @@ register(StructArrayLayout5i10, 'StructArrayLayout5i10'); * * @private */ -class StructArrayLayout2i4ub1f12 extends StructArray { +class StructArrayLayout2i4ub1f12 extends StructArray implements IStructArrayLayout { uint8: Uint8Array; int16: Int16Array; float32: Float32Array; @@ -189,7 +190,7 @@ register(StructArrayLayout2i4ub1f12, 'StructArrayLayout2i4ub1f12'); * * @private */ -class StructArrayLayout4f16 extends StructArray { +class StructArrayLayout4f16 extends StructArray implements IStructArrayLayout { uint8: Uint8Array; float32: Float32Array; @@ -217,6 +218,38 @@ class StructArrayLayout4f16 extends StructArray { StructArrayLayout4f16.prototype.bytesPerElement = 16; register(StructArrayLayout4f16, 'StructArrayLayout4f16'); +/** + * Implementation of the StructArray layout: + * [0]: Float32[2] + * + * @private + */ +class StructArrayLayout2f8 extends StructArray implements IStructArrayLayout { + uint8: Uint8Array; + float32: Float32Array; + + _refreshViews() { + this.uint8 = new Uint8Array(this.arrayBuffer); + this.float32 = new Float32Array(this.arrayBuffer); + } + + emplaceBack(v0: number, v1: number): number { + const i = this.length; + this.resize(i + 1); + return this.emplace(i, v0, v1); + } + + emplace(i: number, v0: number, v1: number): number { + const o4 = i * 2; + this.float32[o4 + 0] = v0; + this.float32[o4 + 1] = v1; + return i; + } +} + +StructArrayLayout2f8.prototype.bytesPerElement = 8; +register(StructArrayLayout2f8, 'StructArrayLayout2f8'); + /** * Implementation of the StructArray layout: * [0]: Uint16[4] @@ -224,7 +257,7 @@ register(StructArrayLayout4f16, 'StructArrayLayout4f16'); * * @private */ -class StructArrayLayout4ui1f12 extends StructArray { +class StructArrayLayout4ui1f12 extends StructArray implements IStructArrayLayout { uint8: Uint8Array; uint16: Uint16Array; float32: Float32Array; @@ -262,7 +295,7 @@ register(StructArrayLayout4ui1f12, 'StructArrayLayout4ui1f12'); * * @private */ -class StructArrayLayout4ui8 extends StructArray { +class StructArrayLayout4ui8 extends StructArray implements IStructArrayLayout { uint8: Uint8Array; uint16: Uint16Array; @@ -296,7 +329,7 @@ register(StructArrayLayout4ui8, 'StructArrayLayout4ui8'); * * @private */ -class StructArrayLayout6i12 extends StructArray { +class StructArrayLayout6i12 extends StructArray implements IStructArrayLayout { uint8: Uint8Array; int16: Int16Array; @@ -334,7 +367,7 @@ register(StructArrayLayout6i12, 'StructArrayLayout6i12'); * * @private */ -class StructArrayLayout4i4ui4i24 extends StructArray { +class StructArrayLayout4i4ui4i24 extends StructArray implements IStructArrayLayout { uint8: Uint8Array; int16: Int16Array; uint16: Uint16Array; @@ -379,7 +412,7 @@ register(StructArrayLayout4i4ui4i24, 'StructArrayLayout4i4ui4i24'); * * @private */ -class StructArrayLayout3i3f20 extends StructArray { +class StructArrayLayout3i3f20 extends StructArray implements IStructArrayLayout { uint8: Uint8Array; int16: Int16Array; float32: Float32Array; @@ -418,7 +451,7 @@ register(StructArrayLayout3i3f20, 'StructArrayLayout3i3f20'); * * @private */ -class StructArrayLayout1ul4 extends StructArray { +class StructArrayLayout1ul4 extends StructArray implements IStructArrayLayout { uint8: Uint8Array; uint32: Uint32Array; @@ -449,7 +482,7 @@ register(StructArrayLayout1ul4, 'StructArrayLayout1ul4'); * * @private */ -class StructArrayLayout2ui4 extends StructArray { +class StructArrayLayout2ui4 extends StructArray implements IStructArrayLayout { uint8: Uint8Array; uint16: Uint16Array; @@ -485,7 +518,7 @@ register(StructArrayLayout2ui4, 'StructArrayLayout2ui4'); * * @private */ -class StructArrayLayout5i4f1i1ul2ui40 extends StructArray { +class StructArrayLayout5i4f1i1ul2ui40 extends StructArray implements IStructArrayLayout { uint8: Uint8Array; int16: Int16Array; float32: Float32Array; @@ -537,7 +570,7 @@ register(StructArrayLayout5i4f1i1ul2ui40, 'StructArrayLayout5i4f1i1ul2ui40'); * * @private */ -class StructArrayLayout3i2i2i16 extends StructArray { +class StructArrayLayout3i2i2i16 extends StructArray implements IStructArrayLayout { uint8: Uint8Array; int16: Int16Array; @@ -576,7 +609,7 @@ register(StructArrayLayout3i2i2i16, 'StructArrayLayout3i2i2i16'); * * @private */ -class StructArrayLayout2f1f2i16 extends StructArray { +class StructArrayLayout2f1f2i16 extends StructArray implements IStructArrayLayout { uint8: Uint8Array; float32: Float32Array; int16: Int16Array; @@ -615,7 +648,7 @@ register(StructArrayLayout2f1f2i16, 'StructArrayLayout2f1f2i16'); * * @private */ -class StructArrayLayout2ub2f12 extends StructArray { +class StructArrayLayout2ub2f12 extends StructArray implements IStructArrayLayout { uint8: Uint8Array; float32: Float32Array; @@ -644,46 +677,13 @@ class StructArrayLayout2ub2f12 extends StructArray { StructArrayLayout2ub2f12.prototype.bytesPerElement = 12; register(StructArrayLayout2ub2f12, 'StructArrayLayout2ub2f12'); -/** - * Implementation of the StructArray layout: - * [0]: Float32[3] - * - * @private - */ -class StructArrayLayout3f12 extends StructArray { - uint8: Uint8Array; - float32: Float32Array; - - _refreshViews() { - this.uint8 = new Uint8Array(this.arrayBuffer); - this.float32 = new Float32Array(this.arrayBuffer); - } - - emplaceBack(v0: number, v1: number, v2: number): number { - const i = this.length; - this.resize(i + 1); - return this.emplace(i, v0, v1, v2); - } - - emplace(i: number, v0: number, v1: number, v2: number): number { - const o4 = i * 3; - this.float32[o4 + 0] = v0; - this.float32[o4 + 1] = v1; - this.float32[o4 + 2] = v2; - return i; - } -} - -StructArrayLayout3f12.prototype.bytesPerElement = 12; -register(StructArrayLayout3f12, 'StructArrayLayout3f12'); - /** * Implementation of the StructArray layout: * [0]: Uint16[3] * * @private */ -class StructArrayLayout3ui6 extends StructArray { +class StructArrayLayout3ui6 extends StructArray implements IStructArrayLayout { uint8: Uint8Array; uint16: Uint16Array; @@ -725,7 +725,7 @@ register(StructArrayLayout3ui6, 'StructArrayLayout3ui6'); * * @private */ -class StructArrayLayout3i2f2ui3ul3ui2f3ub1ul1i1ub60 extends StructArray { +class StructArrayLayout3i2f2ui3ul3ui2f3ub1ul1i1ub60 extends StructArray implements IStructArrayLayout { uint8: Uint8Array; int16: Int16Array; float32: Float32Array; @@ -789,7 +789,7 @@ register(StructArrayLayout3i2f2ui3ul3ui2f3ub1ul1i1ub60, 'StructArrayLayout3i2f2u * * @private */ -class StructArrayLayout2f9i15ui1ul4f1ub80 extends StructArray { +class StructArrayLayout2f9i15ui1ul4f1ub80 extends StructArray implements IStructArrayLayout { uint8: Uint8Array; float32: Float32Array; int16: Int16Array; @@ -859,7 +859,7 @@ register(StructArrayLayout2f9i15ui1ul4f1ub80, 'StructArrayLayout2f9i15ui1ul4f1ub * * @private */ -class StructArrayLayout1f4 extends StructArray { +class StructArrayLayout1f4 extends StructArray implements IStructArrayLayout { uint8: Uint8Array; float32: Float32Array; @@ -890,7 +890,7 @@ register(StructArrayLayout1f4, 'StructArrayLayout1f4'); * * @private */ -class StructArrayLayout5f20 extends StructArray { +class StructArrayLayout5f20 extends StructArray implements IStructArrayLayout { uint8: Uint8Array; float32: Float32Array; @@ -925,7 +925,7 @@ register(StructArrayLayout5f20, 'StructArrayLayout5f20'); * * @private */ -class StructArrayLayout7f28 extends StructArray { +class StructArrayLayout7f28 extends StructArray implements IStructArrayLayout { uint8: Uint8Array; float32: Float32Array; @@ -963,7 +963,7 @@ register(StructArrayLayout7f28, 'StructArrayLayout7f28'); * * @private */ -class StructArrayLayout1ul3ui12 extends StructArray { +class StructArrayLayout1ul3ui12 extends StructArray implements IStructArrayLayout { uint8: Uint8Array; uint32: Uint32Array; uint16: Uint16Array; @@ -1000,7 +1000,7 @@ register(StructArrayLayout1ul3ui12, 'StructArrayLayout1ul3ui12'); * * @private */ -class StructArrayLayout1ui2 extends StructArray { +class StructArrayLayout1ui2 extends StructArray implements IStructArrayLayout { uint8: Uint8Array; uint16: Uint16Array; @@ -1027,11 +1027,11 @@ register(StructArrayLayout1ui2, 'StructArrayLayout1ui2'); /** * Implementation of the StructArray layout: - * [0]: Float32[2] + * [0]: Float32[3] * * @private */ -class StructArrayLayout2f8 extends StructArray { +class StructArrayLayout3f12 extends StructArray implements IStructArrayLayout { uint8: Uint8Array; float32: Float32Array; @@ -1040,22 +1040,23 @@ class StructArrayLayout2f8 extends StructArray { this.float32 = new Float32Array(this.arrayBuffer); } - emplaceBack(v0: number, v1: number): number { + emplaceBack(v0: number, v1: number, v2: number): number { const i = this.length; this.resize(i + 1); - return this.emplace(i, v0, v1); + return this.emplace(i, v0, v1, v2); } - emplace(i: number, v0: number, v1: number): number { - const o4 = i * 2; + emplace(i: number, v0: number, v1: number, v2: number): number { + const o4 = i * 3; this.float32[o4 + 0] = v0; this.float32[o4 + 1] = v1; + this.float32[o4 + 2] = v2; return i; } } -StructArrayLayout2f8.prototype.bytesPerElement = 8; -register(StructArrayLayout2f8, 'StructArrayLayout2f8'); +StructArrayLayout3f12.prototype.bytesPerElement = 12; +register(StructArrayLayout3f12, 'StructArrayLayout3f12'); /** * Implementation of the StructArray layout: @@ -1063,7 +1064,7 @@ register(StructArrayLayout2f8, 'StructArrayLayout2f8'); * * @private */ -class StructArrayLayout16f64 extends StructArray { +class StructArrayLayout16f64 extends StructArray implements IStructArrayLayout { uint8: Uint8Array; float32: Float32Array; @@ -1110,7 +1111,7 @@ register(StructArrayLayout16f64, 'StructArrayLayout16f64'); * * @private */ -class StructArrayLayout4ui3f20 extends StructArray { +class StructArrayLayout4ui3f20 extends StructArray implements IStructArrayLayout { uint8: Uint8Array; uint16: Uint16Array; float32: Float32Array; @@ -1144,13 +1145,44 @@ class StructArrayLayout4ui3f20 extends StructArray { StructArrayLayout4ui3f20.prototype.bytesPerElement = 20; register(StructArrayLayout4ui3f20, 'StructArrayLayout4ui3f20'); +/** + * Implementation of the StructArray layout: + * [0]: Int16[1] + * + * @private + */ +class StructArrayLayout1i2 extends StructArray implements IStructArrayLayout { + uint8: Uint8Array; + int16: Int16Array; + + _refreshViews() { + this.uint8 = new Uint8Array(this.arrayBuffer); + this.int16 = new Int16Array(this.arrayBuffer); + } + + emplaceBack(v0: number): number { + const i = this.length; + this.resize(i + 1); + return this.emplace(i, v0); + } + + emplace(i: number, v0: number): number { + const o2 = i * 1; + this.int16[o2 + 0] = v0; + return i; + } +} + +StructArrayLayout1i2.prototype.bytesPerElement = 2; +register(StructArrayLayout1i2, 'StructArrayLayout1i2'); + /** * Implementation of the StructArray layout: * [0]: Uint8[1] * * @private */ -class StructArrayLayout1ub1 extends StructArray { +class StructArrayLayout1ub1 extends StructArray implements IStructArrayLayout { uint8: Uint8Array; _refreshViews() { @@ -1393,6 +1425,7 @@ export { StructArrayLayout5i10, StructArrayLayout2i4ub1f12, StructArrayLayout4f16, + StructArrayLayout2f8, StructArrayLayout4ui1f12, StructArrayLayout4ui8, StructArrayLayout6i12, @@ -1404,7 +1437,6 @@ export { StructArrayLayout3i2i2i16, StructArrayLayout2f1f2i16, StructArrayLayout2ub2f12, - StructArrayLayout3f12, StructArrayLayout3ui6, StructArrayLayout3i2f2ui3ul3ui2f3ub1ul1i1ub60, StructArrayLayout2f9i15ui1ul4f1ub80, @@ -1413,9 +1445,10 @@ export { StructArrayLayout7f28, StructArrayLayout1ul3ui12, StructArrayLayout1ui2, - StructArrayLayout2f8, + StructArrayLayout3f12, StructArrayLayout16f64, StructArrayLayout4ui3f20, + StructArrayLayout1i2, StructArrayLayout1ub1, StructArrayLayout2i4 as PosArray, StructArrayLayout3i6 as PosGlobeExtArray, @@ -1427,6 +1460,7 @@ export { StructArrayLayout2i4 as HeatmapLayoutArray, StructArrayLayout2i4ub1f12 as LineLayoutArray, StructArrayLayout4f16 as LineExtLayoutArray, + StructArrayLayout2f8 as LinePatternLayoutArray, StructArrayLayout4ui1f12 as PatternLayoutArray, StructArrayLayout4ui8 as DashLayoutArray, StructArrayLayout6i12 as FillExtrusionExtArray, @@ -1438,7 +1472,7 @@ export { StructArrayLayout3i2i2i16 as CollisionBoxLayoutArray, StructArrayLayout2f1f2i16 as CollisionCircleLayoutArray, StructArrayLayout2ub2f12 as CollisionVertexArray, - StructArrayLayout3f12 as CollisionVertexExtArray, + StructArrayLayout4f16 as CollisionVertexExtArray, StructArrayLayout3ui6 as QuadTriangleArray, StructArrayLayout1f4 as ZOffsetVertexArray, StructArrayLayout5f20 as GlobeVertexArray, @@ -1456,6 +1490,7 @@ export { StructArrayLayout3f12 as NormalLayoutArray, StructArrayLayout16f64 as InstanceVertexArray, StructArrayLayout4ui3f20 as FeatureVertexArray, + StructArrayLayout1i2 as ParticleIndexLayoutArray, StructArrayLayout1ub1 as FillExtrusionHiddenByLandmarkArray, StructArrayLayout6i12 as CircleGlobeExtArray }; diff --git a/src/data/bucket.js b/src/data/bucket.js index 1657fecfbba..9e7706bb11a 100644 --- a/src/data/bucket.js +++ b/src/data/bucket.js @@ -1,5 +1,9 @@ // @flow +// Import FeatureIndex as a module with side effects to ensure +// it's registered as a serializable class on the main thread +import './feature_index.js'; + import type {CollisionBoxArray} from './array_types.js'; import type Style from '../style/style.js'; import type {TypedStyleLayer} from '../style/style_layer/typed_style_layer.js'; @@ -120,8 +124,8 @@ export function deserialize(input: Array, style: Style): {[_: string]: B // look up StyleLayer objects from layer ids (since we don't // want to waste time serializing/copying them from the worker) (bucket: any).layers = layers; - if ((bucket: any).stateDependentLayerIds) { - (bucket: any).stateDependentLayers = (bucket: any).stateDependentLayerIds.map((lId) => layers.filter((l) => l.id === lId)[0]); + if (bucket.stateDependentLayerIds) { + (bucket: any).stateDependentLayers = bucket.stateDependentLayerIds.map((lId) => layers.filter((l) => l.id === lId)[0]); } for (const layer of layers) { output[layer.fqid] = bucket; diff --git a/src/data/bucket/fill_extrusion_bucket.js b/src/data/bucket/fill_extrusion_bucket.js index 0200361356a..4d2deffb261 100644 --- a/src/data/bucket/fill_extrusion_bucket.js +++ b/src/data/bucket/fill_extrusion_bucket.js @@ -9,7 +9,6 @@ import {TriangleIndexArray} from '../index_array_type.js'; import EXTENT from '../../style-spec/data/extent.js'; import earcut from 'earcut'; import {VectorTileFeature} from '@mapbox/vector-tile'; -import type {Feature} from "../../style-spec/expression"; const vectorTileFeatureTypes = VectorTileFeature.types; import classifyRings from '../../util/classify_rings.js'; import assert from 'assert'; @@ -25,9 +24,15 @@ import {lngFromMercatorX, latFromMercatorY, mercatorYfromLat, tileToMeter} from import {subdividePolygons} from '../../util/polygon_clipping.js'; import {ReplacementSource, regionsEquals, footprintTrianglesIntersect} from '../../../3d-style/source/replacement_source.js'; import {clamp} from '../../util/util.js'; +import {earthRadius} from '../../geo/lng_lat.js'; +import {Aabb, Frustum} from '../../util/primitives.js'; +import {Elevation} from '../../terrain/elevation.js'; + +import type {Feature} from "../../style-spec/expression"; import type {ClippedPolygon} from '../../util/polygon_clipping.js'; import type {Vec3} from 'gl-matrix'; import type {CanonicalTileID, OverscaledTileID} from '../../source/tile_id.js'; +import type {Segment} from '../segment.js'; import type { Bucket, BucketParameters, @@ -35,8 +40,6 @@ import type { IndexedFeature, PopulateParameters } from '../bucket.js'; -import {earthRadius} from '../../geo/lng_lat.js'; - import type FillExtrusionStyleLayer from '../../style/style_layer/fill_extrusion_style_layer.js'; import type Context from '../../gl/context.js'; import type IndexBuffer from '../../gl/index_buffer.js'; @@ -137,6 +140,8 @@ export class PartData { flags: number; footprintSegIdx: number; footprintSegLen: number; + polygonSegIdx: number; + polygonSegLen: number; min: Point; max: Point; height: number; @@ -150,6 +155,8 @@ export class PartData { this.flags = 0; this.footprintSegIdx = -1; this.footprintSegLen = 0; + this.polygonSegIdx = -1; + this.polygonSegLen = 0; this.min = new Point(Number.MAX_VALUE, Number.MAX_VALUE); this.max = new Point(-Number.MAX_VALUE, -Number.MAX_VALUE); this.height = 0; @@ -570,6 +577,25 @@ export class GroundEffect { } } +type PolygonSegment = { + triangleArrayOffset: number; + triangleCount: number; + triangleSegIdx: number; +}; + +type SegmentedFeature = { + centroidIdx: number; + subtile: number; + polygonSegmentIdx: number; + triangleSegmentIdx: number; +}; + +type TriangleSubSegment = { + segment: Segment; + min: Point; + max: Point; +}; + class FillExtrusionBucket implements Bucket { index: number; zoom: number; @@ -619,6 +645,9 @@ class FillExtrusionBucket implements Bucket { maxHeight: number; + triangleSubSegments: Array; + polygonSegments: Array; + constructor(options: BucketParameters) { this.zoom = options.zoom; this.canonical = options.canonical; @@ -648,6 +677,8 @@ class FillExtrusionBucket implements Bucket { this.groundEffect = new GroundEffect(options); this.maxHeight = 0; this.partLookup = {}; + this.triangleSubSegments = []; + this.polygonSegments = []; } populate(features: Array, options: PopulateParameters, canonical: CanonicalTileID, tileTransform: TileTransform) { @@ -658,14 +689,12 @@ class FillExtrusionBucket implements Bucket { this.borderDoneWithNeighborZ = [-1, -1, -1, -1]; this.tileToMeter = tileToMeter(canonical); this.edgeRadius = this.layers[0].layout.get('fill-extrusion-edge-radius') / this.tileToMeter; - for (const {feature, id, index, sourceLayerIndex} of features) { const needGeometry = this.layers[0]._featureFilter.needGeometry; const evaluationFeature = toEvaluationFeature(feature, needGeometry); // $FlowFixMe[method-unbinding] if (!this.layers[0]._featureFilter.filter(new EvaluationParameters(this.zoom), evaluationFeature, canonical)) continue; - const bucketFeature: BucketFeature = { id, sourceLayerIndex, @@ -686,7 +715,12 @@ class FillExtrusionBucket implements Bucket { options.featureIndex.insert(feature, bucketFeature.geometry, index, sourceLayerIndex, this.index, vertexArrayOffset); } this.sortBorders(); + if (this.projection.name === "mercator") { + this.splitToSubtiles(); + } this.groundEffect.prepareBorderSegments(); + // Clear polygon segment array + this.polygonSegments.length = 0; } addFeatures(options: PopulateParameters, canonical: CanonicalTileID, imagePositions: SpritePositions, availableImages: Array, tileTransform: TileTransform, brightness: ?number) { @@ -695,6 +729,9 @@ class FillExtrusionBucket implements Bucket { this.addFeature(feature, geometry, feature.index, canonical, imagePositions, availableImages, tileTransform, brightness); } this.sortBorders(); + if (this.projection.name === "mercator") { + this.splitToSubtiles(); + } } update(states: FeatureStates, vtLayer: IVectorTileLayer, availableImages: Array, imagePositions: SpritePositions, brightness: ?number) { @@ -770,6 +807,7 @@ class FillExtrusionBucket implements Bucket { const borderCentroidData = new BorderCentroidData(); borderCentroidData.centroidDataIndex = this.centroidData.length; const centroid = new PartData(); + const base = this.layers[0].paint.get('fill-extrusion-base').evaluate(feature, {}, canonical); const onGround = base <= 0; const height = this.layers[0].paint.get('fill-extrusion-height').evaluate(feature, {}, canonical); @@ -780,7 +818,6 @@ class FillExtrusionBucket implements Bucket { if (isGlobe && !this.layoutVertexExtArray) { this.layoutVertexExtArray = new FillExtrusionExtArray(); } - const polygons = classifyRings(geometry, EARCUT_MAX_RINGS); for (let i = polygons.length - 1; i >= 0; i--) { @@ -836,6 +873,13 @@ class FillExtrusionBucket implements Bucket { centroid.footprintSegIdx = this.footprintSegments.length; } + if (centroid.polygonSegIdx < 0) { + centroid.polygonSegIdx = this.polygonSegments.length; + } + + // Store location of generated triangles for the future use + const polygonSeg = {triangleArrayOffset: this.indexArray.length, triangleCount: 0, triangleSegIdx: this.segments.segments.length - 1}; + const fpSegment = new FootprintSegment(); fpSegment.vertexOffset = this.footprintVertices.length; fpSegment.indexOffset = this.footprintIndices.length * 3; @@ -1095,7 +1139,10 @@ class FillExtrusionBucket implements Bucket { } } this.footprintSegments.push(fpSegment); + polygonSeg.triangleCount = this.indexArray.length - polygonSeg.triangleArrayOffset; + this.polygonSegments.push(polygonSeg); ++centroid.footprintSegLen; + ++centroid.polygonSegLen; } assert(!isGlobe || (this.layoutVertexExtArray && this.layoutVertexExtArray.length === this.layoutVertexArray.length)); @@ -1136,6 +1183,182 @@ class FillExtrusionBucket implements Bucket { } } + splitToSubtiles() { + // Split fill extrusion features into 4 "sub-tiles" each of which can + // be rendered separately. This allows more effective rendering as primitives + // sent to GPU can be reduced by a large margin if most of the tile is outside + // of the screen. + // + // Triangles of each feature are sorted into correct sub-tile based on their centroid + // positions. These tiles are in memory in clockwise order so it's possible to + // render almost every possible combination with just a single draw call. + const segmentedFeatures: Array = []; + + for (let centroidIdx = 0; centroidIdx < this.centroidData.length; centroidIdx++) { + const part = this.centroidData[centroidIdx]; + const right = +((part.min.x + part.max.x) > EXTENT); + const bottom = +((part.min.y + part.max.y) > EXTENT); + const subtile = bottom * 2 + (right ^ bottom); + for (let i = 0; i < part.polygonSegLen; i++) { + const polySegIdx = part.polygonSegIdx + i; + segmentedFeatures.push({centroidIdx, subtile, polygonSegmentIdx: polySegIdx, triangleSegmentIdx: this.polygonSegments[polySegIdx].triangleSegIdx}); + } + } + // Sort features based on their subtile index. Triangles can't be moved across + // orignal segment boundaries due to different baseVertex values. + const sortedTriangles = new TriangleIndexArray(); + segmentedFeatures.sort((a, b) => a.triangleSegmentIdx === b.triangleSegmentIdx ? a.subtile - b.subtile : a.triangleSegmentIdx - b.triangleSegmentIdx); + let segmentIdx = 0; + let segmentBeginIndex = 0; + let segmentEndIndex = 0; + for (const segmentedFeature of segmentedFeatures) { + if (segmentedFeature.triangleSegmentIdx !== segmentIdx) { + break; + } + segmentEndIndex++; + } + + const segmentedFeaturesEndIndex = segmentedFeatures.length; + + while (segmentBeginIndex !== segmentedFeatures.length) { + segmentIdx = segmentedFeatures[segmentBeginIndex].triangleSegmentIdx; + // For each feature of this triangle segment (as in all triangles in `Segments[triSegIdx]`), + // find the number of triangles in each subtile and construct new segments for rendering + let subTileIdx = 0; + let featuresBeginIndex = segmentBeginIndex; + let featuresEndIndex = segmentBeginIndex; + + for (let seg = featuresBeginIndex; seg < segmentEndIndex; seg++) { + if (segmentedFeatures[seg].subtile !== subTileIdx) { + break; + } + featuresEndIndex++; + } + while (featuresBeginIndex !== segmentEndIndex) { + const featuresBegin = segmentedFeatures[featuresBeginIndex]; + subTileIdx = featuresBegin.subtile; + const subtileMin = this.centroidData[featuresBegin.centroidIdx].min.clone(); + const subtileMax = this.centroidData[featuresBegin.centroidIdx].max.clone(); + + // Add triangles of this subtile and construct a segment for rendering + const segment: Segment = {vertexOffset: this.segments.segments[segmentIdx].vertexOffset, + primitiveOffset: sortedTriangles.length, + vertexLength: this.segments.segments[segmentIdx].vertexLength, + primitiveLength: 0, + sortKey: undefined, + vaos: {}}; + + for (let featureIdx = featuresBeginIndex; featureIdx < featuresEndIndex; featureIdx++) { + + const feature = segmentedFeatures[featureIdx]; + const data = this.polygonSegments[feature.polygonSegmentIdx]; + const centroidMin = this.centroidData[feature.centroidIdx].min; + const centroidMax = this.centroidData[feature.centroidIdx].max; + const iArray = this.indexArray.uint16; + for (let i = data.triangleArrayOffset; i < data.triangleArrayOffset + data.triangleCount; i++) { + sortedTriangles.emplaceBack(iArray[i * 3], iArray[i * 3 + 1], iArray[i * 3 + 2]); + } + segment.primitiveLength += data.triangleCount; + subtileMin.x = Math.min(subtileMin.x, centroidMin.x); + subtileMin.y = Math.min(subtileMin.y, centroidMin.y); + subtileMax.x = Math.max(subtileMax.x, centroidMax.x); + subtileMax.y = Math.max(subtileMax.y, centroidMax.y); + } + if (segment.primitiveLength > 0) { + this.triangleSubSegments.push({segment, min: subtileMin, max: subtileMax}); + } + featuresBeginIndex = featuresEndIndex; + for (let seg = featuresBeginIndex; seg < segmentEndIndex; seg++) { + if (segmentedFeatures[seg].subtile !== segmentedFeatures[featuresBeginIndex].subtile) { + break; + } + featuresEndIndex++; + } + } + + segmentBeginIndex = segmentEndIndex; + for (let seg = segmentBeginIndex; seg < segmentedFeaturesEndIndex; seg++) { + if (segmentedFeatures[seg].triangleSegmentIdx !== segmentedFeatures[segmentBeginIndex].triangleSegmentIdx) { + break; + } + segmentEndIndex++; + } + } + sortedTriangles._trim(); + this.indexArray = sortedTriangles; + } + + getVisibleSegments(renderId: OverscaledTileID, elevation: ?Elevation, frustum: Frustum): SegmentVector { + let minZ = 0; + let maxZ = 0; + const tiles = 1 << renderId.canonical.z; + + if (elevation) { + const minmax = elevation.getMinMaxForTile(renderId); + if (minmax) { + minZ = minmax.min; + maxZ = minmax.max; + } + } + maxZ += this.maxHeight; + + const id = renderId.toUnwrapped(); + + // Go through sub-tiles and merge visible ones that are also adjacent in memory + // into single segments. + let activeSegment: ?Segment; + const tileMin = [(id.canonical.x / tiles) + id.wrap, (id.canonical.y / tiles)]; + const tileMax = [((id.canonical.x + 1) / tiles) + id.wrap, ((id.canonical.y + 1) / tiles)]; + + const outSegments = new SegmentVector(); + + const mix = (a: Array, b: Array, c: Array): Array => { + return [(a[0] * (1 - c[0])) + (b[0] * c[0]), (a[1] * (1 - c[1])) + (b[1] * c[1])]; + }; + const fracMin = []; + const fracMax = []; + + for (const subSegment of this.triangleSubSegments) { + // Compute aabb of the subtile + fracMin[0] = subSegment.min.x / EXTENT; + fracMin[1] = subSegment.min.y / EXTENT; + fracMax[0] = subSegment.max.x / EXTENT; + fracMax[1] = subSegment.max.y / EXTENT; + const aabbMin = mix(tileMin, tileMax, fracMin); + const aabbMax = mix(tileMin, tileMax, fracMax); + const aabb = new Aabb([aabbMin[0], aabbMin[1], minZ], [aabbMax[0], aabbMax[1], maxZ]); + if (aabb.intersectsPrecise(frustum) === 0) { + if (activeSegment) { + outSegments.segments.push(activeSegment); + activeSegment = undefined; + } + continue; + } + const renderSegment = subSegment.segment; + if (activeSegment && activeSegment.vertexOffset !== renderSegment.vertexOffset) { + // vertex offset is different between adjacent segments => split to separate segments + outSegments.segments.push(activeSegment); + activeSegment = undefined; + } + if (!activeSegment) { + activeSegment = {vertexOffset: renderSegment.vertexOffset, + primitiveLength: renderSegment.primitiveLength, + vertexLength: renderSegment.vertexLength, + primitiveOffset: renderSegment.primitiveOffset, + sortKey: undefined, + vaos: {} + }; + } else { + activeSegment.vertexLength += renderSegment.vertexLength; + activeSegment.primitiveLength += renderSegment.primitiveLength; + } + } + if (activeSegment) { + outSegments.segments.push(activeSegment); + } + return outSegments; + } + // Encoded centroid x and y: // x y // --------------------------------------------- @@ -1151,6 +1374,27 @@ class FillExtrusionBucket implements Bucket { return new Point((clamp(c.x, 1, EXTENT - 1) << 3) | spanX, (clamp(c.y, 1, EXTENT - 1) << 3) | spanY); } + // Border centroid data is unreliable for elevating parts split on tile borders. + // It is used only for synchronous lowering of splits as the centroid (not the size information in split parts) is consistent. + encodeBorderCentroid(borderCentroidData: BorderCentroidData): Point { + assert(borderCentroidData.borders); + if (!borderCentroidData.borders) return new Point(0, 0); + const b = borderCentroidData.borders; + const notOnBorder = Number.MAX_VALUE; + const span = 0x6; // any non zero value since span in this is not used in shader + assert(borderCentroidData.intersectsCount() === 1); + if (b[0][0] !== notOnBorder || b[1][0] !== notOnBorder) { + const x = (b[0][0] !== notOnBorder ? 0 : (0x1FFF << 3)) | span; + const index = b[0][0] !== notOnBorder ? 0 : 1; + return new Point(x, (((((b[index][0] + b[index][1]) / 2) | 0) << 3) | span)); + } else { + assert(b[2][0] !== notOnBorder || b[3][0] !== notOnBorder); + const y = (b[2][0] !== notOnBorder ? 0 : (0x1FFF << 3)) | span; + const index = b[2][0] !== notOnBorder ? 2 : 3; + return new Point((((((b[index][0] + b[index][1]) / 2) | 0) << 3) | span), y); + } + } + showCentroid(borderCentroidData: BorderCentroidData) { const c = this.centroidData[borderCentroidData.centroidDataIndex]; c.flags &= HIDDEN_BY_REPLACEMENT; diff --git a/src/data/bucket/line_attributes_pattern.js b/src/data/bucket/line_attributes_pattern.js new file mode 100644 index 00000000000..20be62c5b67 --- /dev/null +++ b/src/data/bucket/line_attributes_pattern.js @@ -0,0 +1,11 @@ +// @flow +import {createLayout} from '../../util/struct_array.js'; + +import type {StructArrayLayout} from '../../util/struct_array.js'; + +const lineLayoutAttributesPattern: StructArrayLayout = createLayout([ + {name: 'a_pattern_data', components: 2, type: 'Float32'} +]); + +export default lineLayoutAttributesPattern; +export const {members, size, alignment} = lineLayoutAttributesPattern; diff --git a/src/data/bucket/line_bucket.js b/src/data/bucket/line_bucket.js index 2c597cabeb7..cbeb7f8d1ed 100644 --- a/src/data/bucket/line_bucket.js +++ b/src/data/bucket/line_bucket.js @@ -1,9 +1,10 @@ // @flow -import {LineLayoutArray, LineExtLayoutArray} from '../array_types.js'; +import {LineLayoutArray, LineExtLayoutArray, LinePatternLayoutArray} from '../array_types.js'; import {members as layoutAttributes} from './line_attributes.js'; import {members as layoutAttributesExt} from './line_attributes_ext.js'; +import {members as layoutAttributesPattern} from './line_attributes_pattern.js'; import SegmentVector from '../segment.js'; import {ProgramConfigurationSet} from '../program_configuration.js'; import {TriangleIndexArray} from '../index_array_type.js'; @@ -15,6 +16,11 @@ import {hasPattern, addPatternDependencies} from './pattern_bucket_features.js'; import loadGeometry from '../load_geometry.js'; import toEvaluationFeature from '../evaluation_feature.js'; import EvaluationParameters from '../../style/evaluation_parameters.js'; + +// Import LineAtlas as a module with side effects to ensure +// it's registered as a serializable class on the main thread +import '../../render/line_atlas.js'; + import type {ProjectionSpecification} from '../../style-spec/types.js'; import type {CanonicalTileID} from '../../source/tile_id.js'; import type { @@ -59,6 +65,13 @@ const EXTRUDE_SCALE = 63; const COS_HALF_SHARP_CORNER = Math.cos(75 / 2 * (Math.PI / 180)); const SHARP_CORNER_OFFSET = 15; +/* + * Straight corners are used to reduce vertex count for line-join: none lines. + * If corner angle is less than COS_STRAIGHT_CORNER, we use a miter joint, + * instead of creating a new line segment. The default is 5 degrees. + */ +const COS_STRAIGHT_CORNER = Math.cos(5 * (Math.PI / 180)); + // Angle per triangle for approximating round line joins. const DEG_PER_TRIANGLE = 20; @@ -87,6 +100,10 @@ class LineBucket implements Bucket { e1: number; e2: number; + patternJoinNone: boolean; + segmentStart: number; + segmentPoints: Array; + index: number; zoom: number; overscaling: number; @@ -102,6 +119,8 @@ class LineBucket implements Bucket { layoutVertexBuffer: VertexBuffer; layoutVertexArray2: LineExtLayoutArray; layoutVertexBuffer2: VertexBuffer; + patternVertexArray: LinePatternLayoutArray; + patternVertexBuffer: VertexBuffer; indexArray: TriangleIndexArray; indexBuffer: IndexBuffer; @@ -129,6 +148,7 @@ class LineBucket implements Bucket { this.layoutVertexArray = new LineLayoutArray(); this.layoutVertexArray2 = new LineExtLayoutArray(); + this.patternVertexArray = new LinePatternLayoutArray(); this.indexArray = new TriangleIndexArray(); this.programConfigurations = new ProgramConfigurationSet(options.layers, options.zoom); this.segments = new SegmentVector(); @@ -281,6 +301,9 @@ class LineBucket implements Bucket { if (this.layoutVertexArray2.length !== 0) { this.layoutVertexBuffer2 = context.createVertexBuffer(this.layoutVertexArray2, layoutAttributesExt); } + if (this.patternVertexArray.length !== 0) { + this.patternVertexBuffer = context.createVertexBuffer(this.patternVertexArray, layoutAttributesPattern); + } this.layoutVertexBuffer = context.createVertexBuffer(this.layoutVertexArray, layoutAttributes); this.indexBuffer = context.createIndexBuffer(this.indexArray); } @@ -325,6 +348,11 @@ class LineBucket implements Bucket { this.totalDistance = 0; this.lineSoFar = 0; + const joinNone = join === 'none'; + this.patternJoinNone = this.hasPattern && joinNone; + this.segmentStart = 0; + this.segmentPoints = []; + if (this.lineClips) { this.lineClipsArray.push(this.lineClips); // Calculate the total distance, in tile units, of this tiled line feature @@ -396,6 +424,58 @@ class LineBucket implements Bucket { // non-closed line, so we're doing a straight "join". prevNormal = prevNormal || nextNormal; + // The join if a middle vertex, otherwise the cap. + const middleVertex = prevVertex && nextVertex; + let currentJoin = middleVertex ? join : (isPolygon || joinNone) ? 'butt' : cap; + + // calculate cosines of the angle (and its half) using dot product + const cosAngle = prevNormal.x * nextNormal.x + prevNormal.y * nextNormal.y; + + if (joinNone) { + const endLineSegment = function (bucket: LineBucket) { + if (bucket.patternJoinNone) { + const pointCount = bucket.segmentPoints.length / 2; + const segmentLength = bucket.lineSoFar - bucket.segmentStart; + for (let idx = 0; idx < pointCount; ++idx) { + const pos = bucket.segmentPoints[idx * 2]; + const offsetSign = bucket.segmentPoints[idx * 2 + 1]; + // Integer part contains position in tile units + // Fractional part has offset sign 0.25 = -1, 0.5 = 0, 0.75 = 1 + const posAndOffset = Math.round(pos) + 0.5 + offsetSign * 0.25; + bucket.patternVertexArray.emplaceBack(posAndOffset, segmentLength); + bucket.patternVertexArray.emplaceBack(posAndOffset, segmentLength); + } + + // Reset line segment + bucket.segmentPoints = []; + bucket.segmentStart = bucket.lineSoFar; + } + + bucket.e1 = bucket.e2 = -1; + }; + + if (middleVertex && cosAngle < COS_STRAIGHT_CORNER) { // Not straight corner, create separate line segment + this.updateDistance(prevVertex, currentVertex); + this.addCurrentVertex(currentVertex, prevNormal, 1, 1, segment); + endLineSegment(this); + + // Start new segment + this.addCurrentVertex(currentVertex, nextNormal, -1, -1, segment); + + continue; // Don't apply other geometry generation logic + } else if (prevVertex) { + if (!nextVertex) { // End line string + this.updateDistance(prevVertex, currentVertex); + this.addCurrentVertex(currentVertex, prevNormal, 1, 1, segment); + endLineSegment(this); + + continue; // Don't apply other geometry generation logic + } else { + currentJoin = 'miter'; + } + } + } + // Determine the normal of the join extrusion. It is the angle bisector // of the segments between the previous line and the next line. // In the case of 180° angles, the prev and next normals cancel each other out: @@ -416,8 +496,6 @@ class LineBucket implements Bucket { * */ - // calculate cosines of the angle (and its half) using dot product - const cosAngle = prevNormal.x * nextNormal.x + prevNormal.y * nextNormal.y; const cosHalfAngle = joinNormal.x * nextNormal.x + joinNormal.y * nextNormal.y; // Calculate the length of the miter (the ratio of the miter to the width) @@ -440,10 +518,6 @@ class LineBucket implements Bucket { } } - // The join if a middle vertex, otherwise the cap. - const middleVertex = prevVertex && nextVertex; - let currentJoin = middleVertex ? join : isPolygon ? 'butt' : cap; - if (middleVertex && currentJoin === 'round') { if (miterLength < roundLimit) { currentJoin = 'miter'; @@ -528,19 +602,16 @@ class LineBucket implements Bucket { } else if (currentJoin === 'butt') { this.addCurrentVertex(currentVertex, joinNormal, 0, 0, segment); // butt cap - } else if (currentJoin === 'square') { - const offset = prevVertex ? 1 : -1; // closing or starting square cap - if (!prevVertex) { - this.addCurrentVertex(currentVertex, joinNormal, offset, offset, segment); + this.addCurrentVertex(currentVertex, joinNormal, -1, -1, segment); } // make the cap it's own quad to avoid the cap affecting the line distance this.addCurrentVertex(currentVertex, joinNormal, 0, 0, segment); if (prevVertex) { - this.addCurrentVertex(currentVertex, joinNormal, offset, offset, segment); + this.addCurrentVertex(currentVertex, joinNormal, 1, 1, segment); } } else if (currentJoin === 'round') { @@ -593,6 +664,11 @@ class LineBucket implements Bucket { this.addHalfVertex(p, leftX, leftY, round, false, endLeft, segment); this.addHalfVertex(p, rightX, rightY, round, true, -endRight, segment); + + if (this.patternJoinNone) { + // Real data is inserted after each line segment is finished + this.segmentPoints.push(this.lineSoFar - this.segmentStart, endLeft); + } } addHalfVertex({x, y}: Point, extrudeX: number, extrudeY: number, round: boolean, up: boolean, dir: number, segment: Segment) { diff --git a/src/data/bucket/symbol_attributes.js b/src/data/bucket/symbol_attributes.js index 0877a66a783..3a743e98274 100644 --- a/src/data/bucket/symbol_attributes.js +++ b/src/data/bucket/symbol_attributes.js @@ -38,6 +38,7 @@ export const collisionVertexAttributes: StructArrayLayout = createLayout([ export const collisionVertexAttributesExt: StructArrayLayout = createLayout([ {name: 'a_size_scale', components: 1, type: 'Float32'}, {name: 'a_padding', components: 2, type: 'Float32'}, + {name: 'a_z_offset', components: 1, type: 'Float32'} ]); export const collisionBox: StructArrayLayout = createLayout([ diff --git a/src/data/bucket/symbol_bucket.js b/src/data/bucket/symbol_bucket.js index 956fbf5f1de..47948d2cd5c 100644 --- a/src/data/bucket/symbol_bucket.js +++ b/src/data/bucket/symbol_bucket.js @@ -50,12 +50,14 @@ import ResolvedImage from '../../style-spec/expression/types/resolved_image.js'; import {plugin as globalRTLTextPlugin, getRTLTextPluginStatus} from '../../source/rtl_text_plugin.js'; import {resamplePred} from '../../geo/projection/resample.js'; import {tileCoordToECEF} from '../../geo/projection/globe_util.js'; -import type {ProjectionSpecification} from '../../style-spec/types.js'; import {getProjection} from '../../geo/projection/index.js'; -import type Projection from '../../geo/projection/projection.js'; import {mat4, vec3} from 'gl-matrix'; import assert from 'assert'; +import type SymbolStyleLayer from '../../style/style_layer/symbol_style_layer.js'; +import type {Class} from '../../types/class.js'; +import type {ProjectionSpecification} from '../../style-spec/types.js'; +import type Projection from '../../geo/projection/projection.js'; import type {CanonicalTileID, OverscaledTileID} from '../../source/tile_id.js'; import type { Bucket, @@ -65,7 +67,6 @@ import type { } from '../bucket.js'; import type {CollisionBoxArray, CollisionBox, SymbolInstance, StructArrayLayout1f4} from '../array_types.js'; import type {StructArray, StructArrayMember} from '../../util/struct_array.js'; -import SymbolStyleLayer from '../../style/style_layer/symbol_style_layer.js'; import type Context from '../../gl/context.js'; import type IndexBuffer from '../../gl/index_buffer.js'; import type VertexBuffer from '../../gl/vertex_buffer.js'; @@ -73,6 +74,7 @@ import type {SymbolQuad} from '../../symbol/quads.js'; import type {SizeData} from '../../symbol/symbol_size.js'; import type {FeatureStates} from '../../source/source_state.js'; import type {TileTransform} from '../../geo/projection/tile_transform.js'; + export type SingleCollisionBox = { x1: number; y1: number; @@ -87,6 +89,7 @@ export type SingleCollisionBox = { elevation?: number; tileID?: OverscaledTileID; }; + import type {Mat4, Vec3} from 'gl-matrix'; import type {SpritePositions} from '../../util/image.js'; import type {IVectorTileLayer} from '@mapbox/vector-tile'; @@ -379,7 +382,6 @@ register(CollisionBuffers, 'CollisionBuffers'); * @private */ class SymbolBucket implements Bucket { - static MAX_GLYPHS: number; static addDynamicAttributes: typeof addDynamicAttributes; collisionBoxArray: CollisionBoxArray; @@ -490,8 +492,8 @@ class SymbolBucket implements Bucket { } createArrays() { - this.text = new SymbolBuffers(new ProgramConfigurationSet(this.layers, this.zoom, property => /^text/.test(property))); - this.icon = new SymbolBuffers(new ProgramConfigurationSet(this.layers, this.zoom, property => /^icon/.test(property))); + this.text = new SymbolBuffers(new ProgramConfigurationSet(this.layers, this.zoom, (property) => /^text/.test(property))); + this.icon = new SymbolBuffers(new ProgramConfigurationSet(this.layers, this.zoom, (property) => /^icon/.test(property))); this.glyphOffsetArray = new GlyphOffsetArray(); this.lineVertexArray = new SymbolLineVertexArray(); @@ -907,10 +909,7 @@ class SymbolBucket implements Bucket { arrays.collisionVertexArray.emplaceBack(0, 0, 0, 0); } - arrays.collisionVertexArrayExt.emplaceBack(scale, -box.padding, -box.padding); - arrays.collisionVertexArrayExt.emplaceBack(scale, box.padding, -box.padding); - arrays.collisionVertexArrayExt.emplaceBack(scale, box.padding, box.padding); - arrays.collisionVertexArrayExt.emplaceBack(scale, -box.padding, box.padding); + this._commitDebugCollisionVertexUpdate(arrays.collisionVertexArrayExt, scale, box.padding, symbolInstance.zOffset); this._commitLayoutVertex(arrays.layoutVertexArray, boxTileAnchorX, boxTileAnchorY, boxTileAnchorZ, symbolTileAnchorX, symbolTileAnchorY, new Point(box.x1, box.y1)); this._commitLayoutVertex(arrays.layoutVertexArray, boxTileAnchorX, boxTileAnchorY, boxTileAnchorZ, symbolTileAnchorX, symbolTileAnchorY, new Point(box.x2, box.y1)); @@ -986,11 +985,11 @@ class SymbolBucket implements Bucket { return this.tilePixelRatio * featureSize; } - _commitDebugCollisionVertexUpdate(array: StructArray, scale: number, padding: number) { - array.emplaceBack(scale, -padding, -padding); - array.emplaceBack(scale, padding, -padding); - array.emplaceBack(scale, padding, padding); - array.emplaceBack(scale, -padding, padding); + _commitDebugCollisionVertexUpdate(array: StructArray, scale: number, padding: number, zOffset: number) { + array.emplaceBack(scale, -padding, -padding, zOffset); + array.emplaceBack(scale, padding, -padding, zOffset); + array.emplaceBack(scale, padding, padding, zOffset); + array.emplaceBack(scale, -padding, padding, zOffset); } _updateTextDebugCollisionBoxes(size: any, zoom: number, collisionBoxArray: CollisionBoxArray, startIndex: number, endIndex: number, instance: SymbolInstance) { @@ -998,16 +997,16 @@ class SymbolBucket implements Bucket { const box: CollisionBox = (collisionBoxArray.get(b): any); const scale = this.getSymbolInstanceTextSize(size, instance, zoom, b); const array = this.textCollisionBox.collisionVertexArrayExt; - this._commitDebugCollisionVertexUpdate(array, scale, box.padding); + this._commitDebugCollisionVertexUpdate(array, scale, box.padding, instance.zOffset); } } - _updateIconDebugCollisionBoxes(size: any, zoom: number, collisionBoxArray: CollisionBoxArray, startIndex: number, endIndex: number, symbolIndex: number) { + _updateIconDebugCollisionBoxes(size: any, zoom: number, collisionBoxArray: CollisionBoxArray, startIndex: number, endIndex: number, instance: SymbolInstance) { for (let b = startIndex; b < endIndex; b++) { const box = (collisionBoxArray.get(b)); - const scale = this.getSymbolInstanceIconSize(size, zoom, symbolIndex); + const scale = this.getSymbolInstanceIconSize(size, zoom, instance.placedIconSymbolIndex); const array = this.iconCollisionBox.collisionVertexArrayExt; - this._commitDebugCollisionVertexUpdate(array, scale, box.padding); + this._commitDebugCollisionVertexUpdate(array, scale, box.padding, instance.zOffset); } } @@ -1026,8 +1025,8 @@ class SymbolBucket implements Bucket { const symbolInstance = this.symbolInstances.get(i); this._updateTextDebugCollisionBoxes(textSize, zoom, collisionBoxArray, symbolInstance.textBoxStartIndex, symbolInstance.textBoxEndIndex, symbolInstance); this._updateTextDebugCollisionBoxes(textSize, zoom, collisionBoxArray, symbolInstance.verticalTextBoxStartIndex, symbolInstance.verticalTextBoxEndIndex, symbolInstance); - this._updateIconDebugCollisionBoxes(iconSize, zoom, collisionBoxArray, symbolInstance.iconBoxStartIndex, symbolInstance.iconBoxEndIndex, symbolInstance.placedIconSymbolIndex); - this._updateIconDebugCollisionBoxes(iconSize, zoom, collisionBoxArray, symbolInstance.verticalIconBoxStartIndex, symbolInstance.verticalIconBoxEndIndex, symbolInstance.placedIconSymbolIndex); + this._updateIconDebugCollisionBoxes(iconSize, zoom, collisionBoxArray, symbolInstance.iconBoxStartIndex, symbolInstance.iconBoxEndIndex, symbolInstance); + this._updateIconDebugCollisionBoxes(iconSize, zoom, collisionBoxArray, symbolInstance.verticalIconBoxStartIndex, symbolInstance.verticalIconBoxEndIndex, symbolInstance); } if (this.hasTextCollisionBoxData() && this.textCollisionBox.collisionVertexBufferExt) { @@ -1223,14 +1222,6 @@ register(SymbolBucket, 'SymbolBucket', { omit: ['layers', 'collisionBoxArray', 'features', 'compareText'] }); -// this constant is based on the size of StructArray indexes used in a symbol -// bucket--namely, glyphOffsetArrayStart -// eg the max valid UInt16 is 65,535 -// See https://github.com/mapbox/mapbox-gl-js/issues/2907 for motivation -// lineStartIndex and textBoxStartIndex could potentially be concerns -// but we expect there to be many fewer boxes/lines than glyphs -SymbolBucket.MAX_GLYPHS = 65535; - SymbolBucket.addDynamicAttributes = addDynamicAttributes; export default SymbolBucket; diff --git a/src/data/dem_data.js b/src/data/dem_data.js index 34c75292a25..0297bfda515 100644 --- a/src/data/dem_data.js +++ b/src/data/dem_data.js @@ -3,13 +3,12 @@ import {RGBAImage, Float32Image} from '../util/image.js'; import {warnOnce, clamp} from '../util/util.js'; import {register} from '../util/web_worker_transfer.js'; -import type {DEMSourceEncoding} from '../source/worker_source.js'; import DemMinMaxQuadTree from './dem_tree.js'; import assert from 'assert'; import {CanonicalTileID} from '../source/tile_id.js'; import browser from '../util/browser.js'; -export type DEMEncoding = DEMSourceEncoding | "float"; +import type {DEMSourceEncoding} from '../source/worker_source.js'; // DEMData is a data structure for decoding, backfilling, and storing elevation data for processing in the hillshade shaders // data can be populated either from a pngraw image tile or from serliazed data sent back from a worker. When data is initially @@ -23,8 +22,7 @@ export type DEMEncoding = DEMSourceEncoding | "float"; const unpackVectors = { mapbox: [6553.6, 25.6, 0.1, 10000.0], - terrarium: [256.0, 1.0, 1.0 / 256.0, 32768.0], - float: [1, 1, 1, 1] + terrarium: [256.0, 1.0, 1.0 / 256.0, 32768.0] }; function unpackMapbox(r: number, g: number, b: number): number { @@ -41,14 +39,13 @@ function unpackTerrarium(r: number, g: number, b: number): number { export default class DEMData { uid: number; - pixels: Uint8Array; stride: number; dim: number; - encoding: DEMEncoding; borderReady: boolean; _tree: DemMinMaxQuadTree; _modifiedForSources: {[string]: Array}; _timestamp: number; + pixels: Uint8Array; floatView: Float32Array; get tree(): DemMinMaxQuadTree { if (!this._tree) this._buildQuadTree(); @@ -57,8 +54,8 @@ export default class DEMData { // RGBAImage data has uniform 1px padding on all sides: square tile edge size defines stride // and dim is calculated as stride - 2. - constructor(uid: number, data: ImageData, sourceEncoding: DEMSourceEncoding, - convertToFloat: boolean, borderReady: boolean = false): void { + constructor(uid: number, data: ImageData, sourceEncoding: DEMSourceEncoding, borderReady: boolean = false): void { + // debugger; this.uid = uid; if (data.height !== data.width) throw new RangeError('DEM tiles must be square'); if (sourceEncoding && sourceEncoding !== "mapbox" && sourceEncoding !== "terrarium") return warnOnce( @@ -68,7 +65,7 @@ export default class DEMData { const dim = this.dim = data.height - 2; const values = new Uint32Array(data.data.buffer); this.pixels = new Uint8Array(data.data.buffer); - this.encoding = sourceEncoding || 'mapbox'; + this.floatView = new Float32Array(data.data.buffer); this.borderReady = borderReady; this._modifiedForSources = {}; @@ -93,15 +90,13 @@ export default class DEMData { values[this._idx(dim, dim)] = values[this._idx(dim - 1, dim - 1)]; } - if (convertToFloat) { - const floatView = new Float32Array(data.data.buffer); - const unpack = this.encoding === "terrarium" ? unpackTerrarium : unpackMapbox; - for (let i = 0; i < values.length; ++i) { - const byteIdx = i * 4; - floatView[i] = unpack(this.pixels[byteIdx], this.pixels[byteIdx + 1], this.pixels[byteIdx + 2]); - } - this.encoding = "float"; + // Convert to float + const unpack = sourceEncoding === "terrarium" ? unpackTerrarium : unpackMapbox; + for (let i = 0; i < values.length; ++i) { + const byteIdx = i * 4; + this.floatView[i] = unpack(this.pixels[byteIdx], this.pixels[byteIdx + 1], this.pixels[byteIdx + 2]); } + this._timestamp = browser.now(); } @@ -117,46 +112,21 @@ export default class DEMData { y = clamp(y, -1, this.dim); } const idx = this._idx(x, y); - if (this.encoding === "float") { - if (!this.floatView) { - this.floatView = new Float32Array(this.pixels.buffer); - } - return this.floatView[idx]; - } - const byteIndex = idx * 4; - const unpack = this.encoding === "terrarium" ? unpackTerrarium : unpackMapbox; - return unpack(this.pixels[byteIndex], this.pixels[byteIndex + 1], this.pixels[byteIndex + 2]); + return this.floatView[idx]; } set(x: number, y: number, v: number): number { const idx = this._idx(x, y); - if (this.encoding === "float") { - if (!this.floatView) { - this.floatView = new Float32Array(this.pixels.buffer); - } - const p = this.floatView[idx]; - this.floatView[idx] = v; - return v - p; - } - const byteIndex = idx * 4; - const unpack = this.encoding === "terrarium" ? unpackTerrarium : unpackMapbox; - const p = unpack(this.pixels[byteIndex], this.pixels[byteIndex + 1], this.pixels[byteIndex + 2]); - const packed = DEMData.pack(v, this.encoding === "terrarium" ? "terrarium" : "mapbox"); - this.pixels[byteIndex] = packed[0]; - this.pixels[byteIndex + 1] = packed[1]; - this.pixels[byteIndex + 2] = packed[2]; + const p = this.floatView[idx]; + this.floatView[idx] = v; return v - p; } - static getUnpackVector(encoding: DEMEncoding): [number, number, number, number] { + static getUnpackVector(encoding: DEMSourceEncoding): [number, number, number, number] { return unpackVectors[encoding]; } - get unpackVector(): [number, number, number, number] { - return unpackVectors[this.encoding]; - } - _idx(x: number, y: number): number { if (x < -1 || x >= this.dim + 1 || y < -1 || y >= this.dim + 1) throw new RangeError('out of range source coordinates for DEM data'); return (y + 1) * this.stride + (x + 1); @@ -175,11 +145,7 @@ export default class DEMData { } getPixels(): RGBAImage | Float32Image { - if (this.encoding === 'float') { - return new Float32Image({width: this.stride, height: this.stride}, this.pixels); - } else { - return new RGBAImage({width: this.stride, height: this.stride}, this.pixels); - } + return new Float32Image({width: this.stride, height: this.stride}, this.pixels); } backfillBorder(borderTile: DEMData, dx: number, dy: number): void { diff --git a/src/data/dem_tree.js b/src/data/dem_tree.js index 15fc6226016..da2f08fc1a9 100644 --- a/src/data/dem_tree.js +++ b/src/data/dem_tree.js @@ -1,10 +1,10 @@ // @flow -import DEMData from "./dem_data.js"; import {vec3} from 'gl-matrix'; import {number as interpolate} from '../style-spec/util/interpolate.js'; import {clamp} from '../util/util.js'; +import type DEMData from "./dem_data.js"; import type {Vec3} from 'gl-matrix'; class MipLevel { diff --git a/src/data/feature_index.js b/src/data/feature_index.js index c4247ce9776..d675e57019d 100644 --- a/src/data/feature_index.js +++ b/src/data/feature_index.js @@ -31,6 +31,7 @@ import type {TilespaceQueryGeometry} from '../style/query_geometry.js'; import type {FeatureIndex as FeatureIndexStruct} from './array_types.js'; import type {TileTransform} from '../geo/projection/tile_transform.js'; import type {IVectorTileLayer, IVectorTileFeature} from '@mapbox/vector-tile'; +import type {GridIndex} from '../types/grid-index.js'; type QueryParameters = { pixelPosMatrix: Float32Array, @@ -58,7 +59,7 @@ class FeatureIndex { x: number; y: number; z: number; - grid: Grid; + grid: GridIndex; featureIndexArray: FeatureIndexArray; promoteId: ?PromoteIdSpecification; @@ -68,6 +69,7 @@ class FeatureIndex { vtLayers: {[_: string]: IVectorTileLayer}; vtFeatures: {[_: string]: IVectorTileFeature[]}; sourceLayerCoder: DictionaryCoder; + is3DTile: boolean; // no vector source layers constructor(tileID: OverscaledTileID, promoteId?: ?PromoteIdSpecification) { this.tileID = tileID; @@ -77,9 +79,10 @@ class FeatureIndex { this.grid = new Grid(EXTENT, 16, 0); this.featureIndexArray = new FeatureIndexArray(); this.promoteId = promoteId; + this.is3DTile = false; } - insert(feature: IVectorTileFeature, geometry: Array>, featureIndex: number, sourceLayerIndex: number, bucketIndex: number, layoutVertexArrayOffset: number = 0) { + insert(feature: IVectorTileFeature, geometry: Array>, featureIndex: number, sourceLayerIndex: number, bucketIndex: number, layoutVertexArrayOffset: number = 0, envelopePadding: number = 0) { const key = this.featureIndexArray.length; this.featureIndexArray.emplaceBack(featureIndex, sourceLayerIndex, bucketIndex, layoutVertexArrayOffset); @@ -97,6 +100,13 @@ class FeatureIndex { bbox[3] = Math.max(bbox[3], p.y); } + if (envelopePadding !== 0) { + bbox[0] -= envelopePadding; + bbox[1] -= envelopePadding; + bbox[2] += envelopePadding; + bbox[3] += envelopePadding; + } + if (bbox[0] < EXTENT && bbox[1] < EXTENT && bbox[2] >= 0 && @@ -149,6 +159,19 @@ class FeatureIndex { const match = this.featureIndexArray.get(index); let featureGeometry = null; + + if (this.is3DTile) { + // 3D tile is a single bucket tile. + const layerID = this.bucketLayerIDs[0][0]; + const layer = styleLayers[layerID]; + if (layer.type !== "model") continue; + const {queryFeature, intersectionZ} = layer.queryIntersectsMatchingFeature(tilespaceGeometry, match.featureIndex, filter, transform); + if (queryFeature) { + this.appendToResult(result, layerID, match.featureIndex, queryFeature, intersectionZ); + } + continue; + } + this.loadMatchingFeature( result, match, @@ -221,11 +244,6 @@ class FeatureIndex { featureState = sourceFeatureState.getState(styleLayer.sourceLayer || '_geojsonTileLayer', id); } - const serializedLayer = extend({}, serializedLayers[layerID]); - - serializedLayer.paint = evaluateProperties(serializedLayer.paint, styleLayer.paint, feature, featureState, availableImages); - serializedLayer.layout = evaluateProperties(serializedLayer.layout, styleLayer.layout, feature, featureState, availableImages); - const intersectionZ = !intersectionTest || intersectionTest(feature, styleLayer, featureState, layoutVertexArrayOffset); if (!intersectionZ) { // Only applied for non-symbol features @@ -233,14 +251,24 @@ class FeatureIndex { } const geojsonFeature = new GeoJSONFeature(feature, this.z, this.x, this.y, id); + + const serializedLayer = extend({}, serializedLayers[layerID]); + + serializedLayer.paint = evaluateProperties(serializedLayer.paint, styleLayer.paint, feature, featureState, availableImages); + serializedLayer.layout = evaluateProperties(serializedLayer.layout, styleLayer.layout, feature, featureState, availableImages); + geojsonFeature.layer = serializedLayer; - let layerResult = result[layerID]; - if (layerResult === undefined) { - layerResult = result[layerID] = []; - } + this.appendToResult(result, layerID, featureIndex, geojsonFeature, intersectionZ); + } + } - layerResult.push({featureIndex, feature: geojsonFeature, intersectionZ}); + appendToResult(result: QueryResult, layerID: string, featureIndex: number, geojsonFeature: QueryFeature, intersectionZ: boolean | number) { + let layerResult = result[layerID]; + if (layerResult === undefined) { + layerResult = result[layerID] = []; } + + layerResult.push({featureIndex, feature: geojsonFeature, intersectionZ}); } // Given a set of symbol indexes that have already been looked up, diff --git a/src/data/mrt/decompress.js b/src/data/mrt/decompress.js new file mode 100644 index 00000000000..f925b63bdee --- /dev/null +++ b/src/data/mrt/decompress.js @@ -0,0 +1,38 @@ +// @noflow +/* eslint-disable no-undef */ +/* eslint-disable camelcase */ +import {gunzipSync} from 'fflate'; + +/** @typedef { import("./types/types").TCodec } TCodec */ + +/** @type { { [key: string]: string } } */ +const DS_TYPES = {gzip_data: 'gzip'}; + +/** + * Decompress a n array of bytes + * + * @param {Buffer | Uint8Array} bytes - input bytes + * @param {TCodec} codec - codec with which data is compressed + * @return {Promise} Promise which resolves with unzipped data + */ +export default function decompress(bytes, codec) { + // @ts-ignore + if (!globalThis.DecompressionStream) { + switch (codec) { + case 'gzip_data': + return Promise.resolve(gunzipSync(bytes)); + } + } + + const decompressionStreamType = DS_TYPES[codec]; + if (!decompressionStreamType) { + throw new Error(`Unhandled codec: ${codec}`); + } + + /** @ts-ignore */ + const ds = new globalThis.DecompressionStream(decompressionStreamType); + + return new Response(new Blob([bytes]).stream().pipeThrough(ds)) + .arrayBuffer() + .then((buf) => new Uint8Array(buf)); +} diff --git a/src/data/mrt/error.js b/src/data/mrt/error.js new file mode 100644 index 00000000000..78ecdefd914 --- /dev/null +++ b/src/data/mrt/error.js @@ -0,0 +1,14 @@ +// @noflow +class MRTError extends Error { + /** + * @param {string} message - error message + */ + constructor(message) { + super(message); + this.name = 'MRTError'; + } +} + +export default MRTError; + +export {MRTError}; diff --git a/src/data/mrt/filters/bitshuffle.js b/src/data/mrt/filters/bitshuffle.js new file mode 100644 index 00000000000..f4e2d1f5969 --- /dev/null +++ b/src/data/mrt/filters/bitshuffle.js @@ -0,0 +1,43 @@ +// @noflow + +/** @typedef { import("../types/types").TArrayLike } TArrayLike; */ +/** @typedef { import("../types/types").TPixelFormat } TPixelFormat; */ + +/** + * Perform bitshuffle decoding. + * + * @param {TArrayLike} data - flat array of input data + * @param {TPixelFormat} pixelFormat - pixel format of data + * @return {TArrayLike} - zigzag-decoded array + */ +function bitshuffleDecode(data, pixelFormat) { + switch (pixelFormat) { + case 'uint32': + return data; + case 'uint16': + for (let i = 0; i < data.length; i += 2) { + const a = data[i]; + const b = data[i + 1]; + data[i] = ((a & 0xF0) >> 4) | ((a & 0xF000) >> 8) | ((b & 0xF0) << 4) | (b & 0xF000); + data[i + 1] = (a & 0xF) | ((a & 0xF00) >> 4) | ((b & 0xF) << 8) | ((b & 0xF00) << 4); + } + return data; + case 'uint8': + for (let i = 0; i < data.length; i += 4) { + const a = data[i]; + const b = data[i + 1]; + const c = data[i + 2]; + const d = data[i + 3]; + + data[i + 0] = ((a & 0xC0) >> 6) | ((b & 0xC0) >> 4) | ((c & 0xC0) >> 2) | ((d & 0xC0) >> 0); + data[i + 1] = ((a & 0x30) >> 4) | ((b & 0x30) >> 2) | ((c & 0x30) >> 0) | ((d & 0x30) << 2); + data[i + 2] = ((a & 0x0C) >> 2) | ((b & 0x0C) >> 0) | ((c & 0x0C) << 2) | ((d & 0x0C) << 4); + data[i + 3] = ((a & 0x03) >> 0) | ((b & 0x03) << 2) | ((c & 0x03) << 4) | ((d & 0x03) << 6); + } + return data; + default: + throw new Error(`Invalid pixel format, "${pixelFormat}"`); + } +} + +export default bitshuffleDecode; diff --git a/src/data/mrt/filters/delta.js b/src/data/mrt/filters/delta.js new file mode 100644 index 00000000000..21eb2e8c3dd --- /dev/null +++ b/src/data/mrt/filters/delta.js @@ -0,0 +1,51 @@ +// @noflow + +/** @typedef { import("../types/types").TArrayLike } TArrayLike; */ + +/** + * Decode difference-encoded data using a cumulative sum operation along + * the last two (raster row and column) axes. + * + * @param {TArrayLike} data - flat array of input data + * @param {number[]} shape - array dimensions, *including* the pixel dimension, + * i.e. 1, 2, or 4 reflecting whether the data is + * uint32, uint16, or uint8, respectively. + * @return {TArrayLike} - differenced ndarray + */ +function deltaDecode(data, shape) { + if (shape.length !== 4) { + throw new Error( + `Expected data of dimension 4 but got ${shape.length}.` + ); + } + + // Sum over dimensions 1 and 2 of 0, 1, 2, 3 + let axisOffset = shape[3]; + for (let axis = 2; axis >= 1; axis--) { + const start1 = axis === 1 ? 1 : 0; + const start2 = axis === 2 ? 1 : 0; + + for (let i0 = 0; i0 < shape[0]; i0++) { + const offset0 = shape[1] * i0; + + for (let i1 = start1; i1 < shape[1]; i1++) { + const offset1 = shape[2] * (i1 + offset0); + + for (let i2 = start2; i2 < shape[2]; i2++) { + const offset2 = shape[3] * (i2 + offset1); + + for (let i3 = 0; i3 < shape[3]; i3++) { + const offset3 = offset2 + i3; + + data[offset3] += data[offset3 - axisOffset]; + } + } + } + } + axisOffset *= shape[axis]; + } + + return data; +} + +export default deltaDecode; diff --git a/src/data/mrt/filters/zigzag.js b/src/data/mrt/filters/zigzag.js new file mode 100644 index 00000000000..55f4b6dbc17 --- /dev/null +++ b/src/data/mrt/filters/zigzag.js @@ -0,0 +1,36 @@ +// @noflow + +/** @typedef { import("../types/types").TArrayLike } TArrayLike; */ + +/** + * Perform zigzag decoding. + * + * The purpose of this operation is to turn two's complement signed 32-bit + * integers into small positive integers. It does this by performing a + * circular shift and rotating the sign bit all the way over to the least + * significant bit. At the same time, it inverts the bits of negative numbers + * so that all those two's complement ones turn into zeros. + * + * This operation is a bitwise equivalent of the mathematical operation + * + * x % 2 === 1 + * ? (x + 1) / -2 + * : x / 2 + * + * Unlike the bitwise version though, it works throughout the entire 32-bit + * unsigned range without overflow. + * + * Note that this imlementation works on Uint32Array, Uint16Array, and + * Uint8Array input without needing to specially handle the different types. + * + * @param {TArrayLike} data - flat array of input data + * @return {TArrayLike} - zigzag-decoded array + */ +function zigzagDecode(data) { + for (let i = 0, n = data.length; i < n; i++) { + data[i] = (data[i] >>> 1) ^ -(data[i] & 1); + } + return data; +} + +export default zigzagDecode; diff --git a/src/data/mrt/mrt.js b/src/data/mrt/mrt.js new file mode 100644 index 00000000000..d9ce4eb9ff2 --- /dev/null +++ b/src/data/mrt/mrt.js @@ -0,0 +1,486 @@ +// @noflow +/* eslint-disable camelcase */ + +import {TileHeader, NumericData} from './mrt_pbf_decoder.js'; +import {lru} from 'tiny-lru'; +import deltaDecode from './filters/delta.js'; +import zigzagDecode from './filters/zigzag.js'; +import bitshuffleDecode from './filters/bitshuffle.js'; +// eslint-disable-next-line import/no-named-as-default +import MRTError from './error.js'; + +import decompress from './decompress.js'; +import Pbf from 'pbf'; + +/** @typedef { import("pbf") } Pbf; */ +/** @typedef { import("./types/types").TArrayLike } TArrayLike; */ +/** @typedef { import("./types/types").TDataRange } TDataRange; */ +/** @typedef { import("./types/types").TBlockReference } TBlockReference; */ +/** @typedef { import("./types/types").TRasterLayerConfig } TRasterLayerConfig; */ +/** @typedef { import("./types/types").TBandViewRGBA } TBandViewRGBA; */ +/** @typedef { import("./types/types").TPbfRasterTileData } TPbfRasterTileData */ +/** @typedef { import("./types/types").TProcessingTask } TProcessingTask */ +/** @typedef { import("./types/types").TProcessingBatch } TProcessingBatch */ +/** @typedef { import("./types/types").TDecodingResult } TDecodingResult */ +/** @typedef { import("./types/types").TPbfDataIndexEntry } TPbfDataIndexEntry */ +/** @typedef { import("./types/types").TPixelFormat } TPixelFormat */ + +const MRT_VERSION = 1; + +/** @type { { [key: number]: TPixelFormat } } */ +const PIXEL_FORMAT = { + 0: 'uint32', + 1: 'uint32', + 2: 'uint16', + 3: 'uint8', +}; + +const PIXEL_FORMAT_TO_DIM_LEN = { + uint32: 1, + uint16: 2, + uint8: 4, +}; + +const PIXEL_FORMAT_TO_CTOR = { + uint32: Uint32Array, + uint16: Uint16Array, + uint8: Uint8Array, +}; + +class MapboxRasterTile { + /** + * @param {number} cacheSize - number of decoded data chunks cached + */ + constructor(cacheSize = 1) { + this.x = NaN; + this.y = NaN; + this.z = NaN; + + /** @type { { [key: string]: RasterLayer } } */ + this.layers = {}; + + this._cacheSize = cacheSize; + } + + /** + * Get a layer instance by name + * @param {string} layerName - name of requested layer + * @return {RasterLayer?} layer instance + */ + getLayer(layerName) { + return this.layers[layerName]; + } + + getHeaderLength(buf) { + const bytes = new Uint8Array(buf); + const view = new DataView(buf); + if (bytes[0] !== 0x0d) throw new MRTError('File is not a valid MRT.'); + return view.getUint32(1, true); + } + + /** + * @param {ArrayBuffer} buf - data buffer + * @return {MapboxRasterTile} raster tile instance + */ + parseHeader(buf) { + // Validate the magic number + const bytes = new Uint8Array(buf); + // const view = new DataView(buf); + + const headerLength = this.getHeaderLength(buf); + + if (bytes.length < headerLength) { + throw new MRTError( + `Expected header with length >= ${headerLength} but got buffer of length ${bytes.length}` + ); + } + + /** @type {Pbf} */ + const pbf = new Pbf(bytes.subarray(0, headerLength)); + + /** @type {TPbfRasterTileData} */ + const meta = TileHeader.read(pbf); + + // Validate the incoming tile z/x/y matches, if already initialized + if ( + !isNaN(this.x) && + (this.x !== meta.x || this.y !== meta.y || this.z !== meta.z) + ) { + throw new MRTError( + `Invalid attempt to parse header ${meta.z}/${meta.x}/${meta.y} for tile ${this.z}/${this.x}/${this.y}` + ); + } + + this.x = meta.x; + this.y = meta.y; + this.z = meta.z; + + for (const layer of meta.layers) { + this.layers[layer.name] = new RasterLayer(layer, { + cacheSize: this._cacheSize, + }); + } + + return this; + } + + /** + * Create a serializable representation of a data parsing task + * @param {TDataRange} range - range of fetched data + * @return {MRTDecodingBatch} processing task description + */ + createDecodingTask(range) { + /** @type {TProcessingTask[]} */ + const tasks = []; + const layer = this.getLayer(range.layerName); + + for (let i = 0; i < layer.dataIndex.length; i++) { + const block = layer.dataIndex[i]; + const firstByte = block.first_byte - range.firstByte; + const lastByte = block.last_byte + 1 - range.firstByte; + if (i < range.firstBlock || i > range.lastBlock) continue; + if (layer._blocksInProgress.has(i)) continue; + + const task = { + layerName: layer.name, + firstByte, + lastByte, + pixelFormat: layer.pixelFormat, + blockIndex: i, + blockShape: [block.bands.length].concat(layer.bandShape), + buffer: layer.buffer, + codec: block.codec.codec, + filters: block.filters.map((f) => f.filter), + }; + layer._blocksInProgress.add(i); + tasks.push(task); + } + const onCancel = () => { + tasks.forEach((task) => + layer._blocksInProgress.delete(task.blockIndex) + ); + }; + + /** @type {(err: Error, results: TDecodingResult[]) => void} */ + const onComplete = (err, results) => { + tasks.forEach((task) => + layer._blocksInProgress.delete(task.blockIndex) + ); + if (err) throw err; + results.forEach((result) => { + this.getLayer(result.layerName).processDecodedData(result); + }); + }; + + return new MRTDecodingBatch(tasks, onCancel, onComplete); + } +} + +class RasterLayer { + /** + * @param {object} pbf - layer configuration + * @param {number} pbf.version - major version of MRT specification with which tile was encoded + * @param {string} pbf.name - layer name + * @param {string} pbf.units - layer units + * @param {number} pbf.tilesize - number of rows and columns in raster data + * @param {number} pbf.buffer - number of pixels around the edge of each tile + * @param {number} pbf.pixel_format - encoded pixel format enum indicating uint32, uint16, or uint8 + * @param {TPbfDataIndexEntry[]} pbf.data_index - index of data chunk byte offsets + * @param {TRasterLayerConfig} [config] - Additional configuration parameters + */ + constructor( + {version, name, units, tilesize, pixel_format, buffer, data_index}, + config + ) { + // Take these directly from decoded Pbf + this.version = version; + if (this.version !== MRT_VERSION) { + throw new MRTError( + `Cannot parse raster layer encoded with MRT version ${version}` + ); + } + this.name = name; + this.units = units; + this.tileSize = tilesize; + this.buffer = buffer; + this.pixelFormat = PIXEL_FORMAT[pixel_format]; + this.dataIndex = data_index; + + this.bandShape = [ + tilesize + 2 * buffer, + tilesize + 2 * buffer, + PIXEL_FORMAT_TO_DIM_LEN[this.pixelFormat], + ]; + + // Type script is creating more problems than it solves here: + const cacheSize = config ? config.cacheSize : 5; + this._decodedBlocks = lru(cacheSize); + + this._blocksInProgress = new Set(); + } + + /** + * Assimilate results of data loading task + * @param {TDecodingResult} result - result of processing task + */ + processDecodedData(result) { + const key = result.blockIndex.toString(); + if (this._decodedBlocks.get(key)) return; + this._decodedBlocks.set(key, result.data); + } + + /** + * Find block for a band sequence index + * @param {string|number} band - label or integer index of desired band + * @return {TBlockReference} - index of block and index of band within block + */ + getBlockForBand(band) { + let blockBandStart = 0; + switch (typeof band) { + case 'string': + for (const [blockIndex, block] of this.dataIndex.entries()) { + for (const [ + blockBandIndex, + bandName, + ] of block.bands.entries()) { + if (bandName !== band) continue; + return { + bandIndex: blockBandStart + blockBandIndex, + blockIndex, + blockBandIndex, + }; + } + blockBandStart += block.bands.length; + } + break; + case 'number': + for (const [blockIndex, block] of this.dataIndex.entries()) { + if ( + band >= blockBandStart && + band < blockBandStart + block.bands.length + ) { + return { + bandIndex: band, + blockIndex, + blockBandIndex: band - blockBandStart, + }; + } + blockBandStart += block.bands.length; + } + break; + default: + throw new MRTError( + `Invalid band \`${JSON.stringify( + band + )}\`. Expected string or integer.` + ); + } + throw new MRTError(`Band not found: ${JSON.stringify(band)}`); + } + + /** + * Get the byte range of a data slice, for performing a HTTP Range fetch + * @param {number[]} bandList - list of slices to be covered + * @return {TDataRange} range of data + */ + getDataRange(bandList) { + let firstByte = Infinity; + let lastByte = -Infinity; + let firstBlock = Infinity; + let lastBlock = -Infinity; + for (const band of bandList) { + const {blockIndex} = this.getBlockForBand(band); + if (blockIndex < 0) { + throw new MRTError(`Invalid band: ${JSON.stringify(band)}`); + } + const block = this.dataIndex[blockIndex]; + firstBlock = Math.min(firstBlock, blockIndex); + lastBlock = Math.max(lastBlock, blockIndex); + firstByte = Math.min(firstByte, block.first_byte); + lastByte = Math.max(lastByte, block.last_byte); + } + return { + layerName: this.name, + firstByte, + lastByte, + firstBlock, + lastBlock, + }; + } + + hasBand(band) { + const {blockIndex} = this.getBlockForBand(band); + return blockIndex >= 0; + } + + /** + * Check if the layer has data for a given sequence band + * @param {number} band - sequence band + * @return {boolean} true if data is already available + */ + hasDataForBand(band) { + const {blockIndex} = this.getBlockForBand(band); + return blockIndex >= 0 && !!this._decodedBlocks.get(blockIndex.toString()); + } + + /** + * Get a typed array view of data + * @param {number} band - sequence band + * @return {TBandViewRGBA} view of raster layer + */ + getBandView(band) { + const {blockIndex, blockBandIndex} = this.getBlockForBand(band); + + /** @type {Uint8Array} */ + const data = this._decodedBlocks.get(blockIndex.toString()); + + if (!data) { + throw new MRTError( + `Data for band ${JSON.stringify(band)} of layer "${ + this.name + }" not decoded.` + ); + } + + const block = this.dataIndex[blockIndex]; + const bandDataLength = this.bandShape.reduce((a, b) => a * b, 1); + const start = blockBandIndex * bandDataLength; + const view = data.subarray(start, start + bandDataLength); + const bytes = new Uint8Array(view.buffer).subarray(view.byteOffset, view.byteOffset + view.byteLength); + return { + data: view, + bytes, + tileSize: this.tileSize, + buffer: this.buffer, + offset: block.offset, + scale: block.scale, + }; + } +} + +class MRTDecodingBatch { + /** + * @param {TProcessingTask[]} tasks - processing tasks + * @param {() => void} onCancel - callback invoked on cancel + * @param {(err: Error, results: TDecodingResult[]) => void} onComplete - callback invoked on completion + */ + constructor(tasks, onCancel, onComplete) { + this.tasks = tasks; + this._onCancel = onCancel; + this._onComplete = onComplete; + this._finalized = false; + } + + /** + * Cancel a processing task + * return {void} + */ + cancel() { + if (this._finalized) return; + this._onCancel(); + this._finalized = true; + } + + /** + * Complete a processing task + * @param {Error} err - processing error, if encountered + * @param {TDecodingResult[]} result - result of processing + * return {void} + */ + complete(err, result) { + if (this._finalized) return; + this._onComplete(err, result); + this._finalized = true; + } +} + +/** + * Process a data parsing task + * @param {ArrayBufferLike} buf - data buffer + * @param {TProcessingBatch} decodingBatch - data processing task + * @return {Promise} output of processing task + */ +MapboxRasterTile.performDecoding = function (buf, decodingBatch) { + return Promise.all( + decodingBatch.tasks.map((task) => { + const { + layerName, + firstByte, + lastByte, + pixelFormat, + blockShape, + blockIndex, + filters, + codec, + } = task; + + const bytes = new Uint8Array(buf); + const taskBuf = bytes.subarray(firstByte, lastByte + 1); + const dataLength = blockShape[0] * blockShape[1] * blockShape[2]; + const values = new Uint32Array(dataLength); + + let decoded; + + switch (codec) { + case 'gzip_data': { + decoded = decompress(taskBuf, codec).then((bytes) => { + const pbf = NumericData.read(new Pbf(bytes)); + switch (pbf.values) { + case 'uint32_values': { + pbf.uint32_values.readValuesInto(values); + const Ctor = PIXEL_FORMAT_TO_CTOR[pixelFormat]; + return new Ctor(values.buffer); + } + default: + throw new Error( + `Unhandled numeric data "${pbf.values}"` + ); + } + }); + break; + } + default: + throw new Error(`Unhandled codec: ${codec}`); + } + + return decoded + .then((data) => { + // Decode filters, one at a time, in reverse order + for (let i = filters.length - 1; i >= 0; i--) { + switch (filters[i]) { + case 'delta_filter': + deltaDecode(data, blockShape); + break; + case 'zigzag_filter': + zigzagDecode(data); + break; + case 'bitshuffle_filter': + bitshuffleDecode(data, pixelFormat); + break; + default: + throw new Error( + `Unhandled filter "${filters[i]}"` + ); + } + } + + return { + layerName, + blockIndex, + data, + }; + }) + .catch((err) => { + throw err; + }); + }) + ); +}; + +export { + MapboxRasterTile, + MRTError, + RasterLayer, + deltaDecode, + MRTDecodingBatch +}; diff --git a/src/data/mrt/mrt_pbf_decoder.js b/src/data/mrt/mrt_pbf_decoder.js new file mode 100644 index 00000000000..6d9be6a19b9 --- /dev/null +++ b/src/data/mrt/mrt_pbf_decoder.js @@ -0,0 +1,392 @@ +// @noflow +/* eslint-disable camelcase */ +/* eslint-disable no-sequences */ +/* eslint-disable no-unused-vars */ +/* eslint-disable no-unreachable */ +/* eslint-disable no-unused-expressions */ + +/* This code is not entirely auto-generated. It's based on the output of npm run + * pbf:generate, but it has been hand-modified. + * + * There is precisely two section which are customized: A utility function is + * added at the top, and then at the bottom the NumericData.VariableLengthValues + * reader is modified to read values into a preallocated typed array in order + * to avoid the default behavior which reads them into a generic JavaScript + * array only to then pass them into a typed array. + * + * To update this file: + * 1. run `npm run pbf:generate` + * 2. copy the contents of the updated `mrt/js/proto/mrt_v2_pb2.js` into this file + * 3. manually transfer over modified sections + * 4. delete all `write` methods (they're not used in JS) + * 5. remove references to `exports` + */ + +/******************************************************** + **************** BEGIN EDITED SECTION ****************** + ********************************************************/ +const Bytes = 2; + +function readPackedEnd(pbf) { + return pbf.type === Bytes ? pbf.readVarint() + pbf.pos : pbf.pos + 1; +} + +function createPackedVarintReader(pbf) { + if (pbf.type !== Bytes) throw new Error(`Unsupported pbf type "${pbf.type}"`); + const end = readPackedEnd(pbf); + const start = pbf.pos; + pbf.pos = end; + return function readInto(arr) { + pbf.pos = start; + let i = 0; + while (pbf.pos < end) { + const value = pbf.readVarint(); + arr[i++] = value; + } + return arr; + }; +} +/******************************************************** + ***************** END EDITED SECTION ******************* + ********************************************************/ + +// TileHeader ======================================== + +const TileHeader = {}; + +TileHeader.read = function (pbf, end) { + return pbf.readFields( + TileHeader._readField, + {header_length: 0, x: 0, y: 0, z: 0, layers: []}, + end + ); +}; +TileHeader._readField = function (tag, obj, pbf) { + if (tag === 1) obj.header_length = pbf.readFixed32(); + else if (tag === 2) obj.x = pbf.readVarint(); + else if (tag === 3) obj.y = pbf.readVarint(); + else if (tag === 4) obj.z = pbf.readVarint(); + else if (tag === 5) + obj.layers.push(TileHeader.Layer.read(pbf, pbf.readVarint() + pbf.pos)); +}; + +TileHeader.PixelFormat = { + PIXEL_FORMAT_UNKNOWN: { + value: 0, + options: {}, + }, + PIXEL_FORMAT_UINT32: { + value: 1, + options: {}, + }, + PIXEL_FORMAT_UINT16: { + value: 2, + options: {}, + }, + PIXEL_FORMAT_UINT8: { + value: 3, + options: {}, + }, +}; + +// TileHeader.Filter ======================================== + +TileHeader.Filter = {}; + +TileHeader.Filter.read = function (pbf, end) { + return pbf.readFields( + TileHeader.Filter._readField, + { + delta_filter: null, + filter: null, + zigzag_filter: null, + bitshuffle_filter: null, + byteshuffle_filter: null, + }, + end + ); +}; +TileHeader.Filter._readField = function (tag, obj, pbf) { + if (tag === 1) + (obj.delta_filter = TileHeader.Filter.Delta.read( + pbf, + pbf.readVarint() + pbf.pos + )), + (obj.filter = 'delta_filter'); + else if (tag === 2) + (obj.zigzag_filter = TileHeader.Filter.Zigzag.read( + pbf, + pbf.readVarint() + pbf.pos + )), + (obj.filter = 'zigzag_filter'); + else if (tag === 3) + (obj.bitshuffle_filter = TileHeader.Filter.BitShuffle.read( + pbf, + pbf.readVarint() + pbf.pos + )), + (obj.filter = 'bitshuffle_filter'); + else if (tag === 4) + (obj.byteshuffle_filter = TileHeader.Filter.ByteShuffle.read( + pbf, + pbf.readVarint() + pbf.pos + )), + (obj.filter = 'byteshuffle_filter'); +}; + +// TileHeader.Filter.Delta ======================================== + +TileHeader.Filter.Delta = {}; + +TileHeader.Filter.Delta.read = function (pbf, end) { + return pbf.readFields( + TileHeader.Filter.Delta._readField, + {block_size: 0}, + end + ); +}; +TileHeader.Filter.Delta._readField = function (tag, obj, pbf) { + if (tag === 1) obj.block_size = pbf.readVarint(); +}; + +// TileHeader.Filter.Zigzag ======================================== + +TileHeader.Filter.Zigzag = {}; + +TileHeader.Filter.Zigzag.read = function (pbf, end) { + return pbf.readFields(TileHeader.Filter.Zigzag._readField, {}, end); +}; +TileHeader.Filter.Zigzag._readField = function (tag, obj, pbf) {}; + +// TileHeader.Filter.BitShuffle ======================================== + +TileHeader.Filter.BitShuffle = {}; + +TileHeader.Filter.BitShuffle.read = function (pbf, end) { + return pbf.readFields(TileHeader.Filter.BitShuffle._readField, {}, end); +}; +TileHeader.Filter.BitShuffle._readField = function (tag, obj, pbf) {}; + +// TileHeader.Filter.ByteShuffle ======================================== + +TileHeader.Filter.ByteShuffle = {}; + +TileHeader.Filter.ByteShuffle.read = function (pbf, end) { + return pbf.readFields(TileHeader.Filter.ByteShuffle._readField, {}, end); +}; +TileHeader.Filter.ByteShuffle._readField = function (tag, obj, pbf) {}; + +// TileHeader.Codec ======================================== + +TileHeader.Codec = {}; + +TileHeader.Codec.read = function (pbf, end) { + return pbf.readFields( + TileHeader.Codec._readField, + { + gzip_data: null, + codec: null, + jpeg_image: null, + webp_image: null, + png_image: null, + }, + end + ); +}; +TileHeader.Codec._readField = function (tag, obj, pbf) { + if (tag === 1) + (obj.gzip_data = TileHeader.Codec.GzipData.read( + pbf, + pbf.readVarint() + pbf.pos + )), + (obj.codec = 'gzip_data'); + else if (tag === 2) + (obj.jpeg_image = TileHeader.Codec.JpegImage.read( + pbf, + pbf.readVarint() + pbf.pos + )), + (obj.codec = 'jpeg_image'); + else if (tag === 3) + (obj.webp_image = TileHeader.Codec.WebpImage.read( + pbf, + pbf.readVarint() + pbf.pos + )), + (obj.codec = 'webp_image'); + else if (tag === 4) + (obj.png_image = TileHeader.Codec.PngImage.read( + pbf, + pbf.readVarint() + pbf.pos + )), + (obj.codec = 'png_image'); +}; + +// TileHeader.Codec.GzipData ======================================== + +TileHeader.Codec.GzipData = {}; + +TileHeader.Codec.GzipData.read = function (pbf, end) { + return pbf.readFields(TileHeader.Codec.GzipData._readField, {}, end); +}; +TileHeader.Codec.GzipData._readField = function (tag, obj, pbf) {}; + +// TileHeader.Codec.JpegImage ======================================== + +TileHeader.Codec.JpegImage = {}; + +TileHeader.Codec.JpegImage.read = function (pbf, end) { + return pbf.readFields(TileHeader.Codec.JpegImage._readField, {}, end); +}; +TileHeader.Codec.JpegImage._readField = function (tag, obj, pbf) {}; + +// TileHeader.Codec.WebpImage ======================================== + +TileHeader.Codec.WebpImage = {}; + +TileHeader.Codec.WebpImage.read = function (pbf, end) { + return pbf.readFields(TileHeader.Codec.WebpImage._readField, {}, end); +}; +TileHeader.Codec.WebpImage._readField = function (tag, obj, pbf) {}; + +// TileHeader.Codec.PngImage ======================================== + +TileHeader.Codec.PngImage = {}; + +TileHeader.Codec.PngImage.read = function (pbf, end) { + return pbf.readFields(TileHeader.Codec.PngImage._readField, {}, end); +}; +TileHeader.Codec.PngImage._readField = function (tag, obj, pbf) {}; + +// TileHeader.DataIndexEntry ======================================== + +TileHeader.DataIndexEntry = {}; + +TileHeader.DataIndexEntry.read = function (pbf, end) { + return pbf.readFields( + TileHeader.DataIndexEntry._readField, + { + first_byte: 0, + last_byte: 0, + filters: [], + codec: null, + offset: 0, + scale: 0, + bands: [], + }, + end + ); +}; +TileHeader.DataIndexEntry._readField = function (tag, obj, pbf) { + if (tag === 1) obj.first_byte = pbf.readFixed64(); + else if (tag === 2) obj.last_byte = pbf.readFixed64(); + else if (tag === 3) + obj.filters.push( + TileHeader.Filter.read(pbf, pbf.readVarint() + pbf.pos) + ); + else if (tag === 4) + obj.codec = TileHeader.Codec.read(pbf, pbf.readVarint() + pbf.pos); + else if (tag === 5) obj.offset = pbf.readFloat(); + else if (tag === 6) obj.scale = pbf.readFloat(); + else if (tag === 7) obj.bands.push(pbf.readString()); +}; + +// TileHeader.Layer ======================================== + +TileHeader.Layer = {}; + +TileHeader.Layer.read = function (pbf, end) { + return pbf.readFields( + TileHeader.Layer._readField, + { + version: 0, + name: '', + units: '', + tilesize: 0, + buffer: 0, + pixel_format: 0, + data_index: [], + }, + end + ); +}; +TileHeader.Layer._readField = function (tag, obj, pbf) { + if (tag === 1) obj.version = pbf.readVarint(); + else if (tag === 2) obj.name = pbf.readString(); + else if (tag === 3) obj.units = pbf.readString(); + else if (tag === 4) obj.tilesize = pbf.readVarint(); + else if (tag === 5) obj.buffer = pbf.readVarint(); + else if (tag === 6) obj.pixel_format = pbf.readVarint(); + else if (tag === 7) + obj.data_index.push( + TileHeader.DataIndexEntry.read(pbf, pbf.readVarint() + pbf.pos) + ); +}; + +// NumericData ======================================== + +const NumericData = {}; + +NumericData.read = function (pbf, end) { + return pbf.readFields( + NumericData._readField, + {uint32_values: null, values: null, fixed32_values: null}, + end + ); +}; +NumericData._readField = function (tag, obj, pbf) { + if (tag === 2) + (obj.uint32_values = NumericData.Uint32Values.read( + pbf, + pbf.readVarint() + pbf.pos + )), + (obj.values = 'uint32_values'); + else if (tag === 3) + (obj.fixed32_values = NumericData.Fixed32Values.read( + pbf, + pbf.readVarint() + pbf.pos + )), + (obj.values = 'fixed32_values'); +}; + +// NumericData.Uint32Values ======================================== + +NumericData.Uint32Values = {}; + +NumericData.Uint32Values.read = function (pbf, end) { + return pbf.readFields( + NumericData.Uint32Values._readField, + {values: []}, + end + ); +}; +NumericData.Uint32Values._readField = function (tag, obj, pbf) { + /******************************************************** + ***************** BEGIN EDITED SECTION ******************* + ********************************************************/ + if (tag === 1) obj.readValuesInto = createPackedVarintReader(pbf); + /******************************************************** + ***************** END EDITED SECTION ******************* + ********************************************************/ +}; + +// NumericData.Fixed32Values ======================================== + +NumericData.Fixed32Values = {}; + +NumericData.Fixed32Values.read = function (pbf, end) { + return pbf.readFields( + NumericData.Fixed32Values._readField, + {values: []}, + end + ); +}; +NumericData.Fixed32Values._readField = function (tag, obj, pbf) { + /******************************************************** + ***************** BEGIN EDITED SECTION ******************* + ********************************************************/ + throw new Error('Not implemented'); + if (tag === 1) pbf.readPackedFixed32(obj.values); + /******************************************************** + ***************** END EDITED SECTION ******************* + ********************************************************/ +}; + +export {TileHeader, NumericData}; diff --git a/src/data/mrt_data.js b/src/data/mrt_data.js new file mode 100644 index 00000000000..174a2f06a38 --- /dev/null +++ b/src/data/mrt_data.js @@ -0,0 +1,6 @@ +// @flow + +import {register} from '../util/web_worker_transfer.js'; +import {MRTDecodingBatch} from './mrt/mrt.js'; + +register(MRTDecodingBatch, 'MRTDecodingBatch', {omit: ['_onCancel', '_onComplete']}); diff --git a/src/data/particle_attributes.js b/src/data/particle_attributes.js new file mode 100644 index 00000000000..37be2d322f2 --- /dev/null +++ b/src/data/particle_attributes.js @@ -0,0 +1,8 @@ +// @flow + +import {createLayout} from '../util/struct_array.js'; +import type {StructArrayLayout} from '../util/struct_array.js'; + +export default (createLayout([ + {name: 'a_index', type: 'Int16', components: 1} +]): StructArrayLayout); diff --git a/src/data/program_configuration.js b/src/data/program_configuration.js index 3c56053278b..711b029c1d3 100644 --- a/src/data/program_configuration.js +++ b/src/data/program_configuration.js @@ -12,13 +12,13 @@ import dashAttributes from './bucket/dash_attributes.js'; import EvaluationParameters from '../style/evaluation_parameters.js'; import FeaturePositionMap from './feature_position_map.js'; import { - Uniform, Uniform1f, UniformColor, Uniform4f } from '../render/uniform_binding.js'; import assert from 'assert'; +import type {Class} from '../../src/types/class.js'; import type {CanonicalTileID} from '../source/tile_id.js'; import type Context from '../gl/context.js'; import type {TypedStyleLayer} from '../style/style_layer/typed_style_layer.js'; @@ -36,11 +36,12 @@ import type {PossiblyEvaluated} from '../style/properties.js'; import type {FeatureStates} from '../source/source_state.js'; import type {FormattedSection} from '../style-spec/expression/types/formatted.js'; import type {IVectorTileLayer} from '@mapbox/vector-tile'; +import type {IUniform} from '../render/uniform_binding.js'; export type BinderUniform = { name: string, property: string, - binding: Uniform + binding: IUniform }; function packColor(color: Color): [number, number] { @@ -87,8 +88,8 @@ interface AttributeBinder { interface UniformBinder { uniformNames: Array; - setUniform(program: WebGLProgram, uniform: Uniform<*>, globals: GlobalProperties, currentValue: PossiblyEvaluatedPropertyValue<*>, uniformName: string): void; - getBinding(context: Context, name: string): $Shape>; + setUniform(program: WebGLProgram, uniform: IUniform, globals: GlobalProperties, currentValue: PossiblyEvaluatedPropertyValue<*>, uniformName: string): void; + getBinding(context: Context, name: string): $Shape>; } class ConstantBinder implements UniformBinder { @@ -102,12 +103,12 @@ class ConstantBinder implements UniformBinder { this.type = type; } - setUniform(program: WebGLProgram, uniform: Uniform<*>, globals: GlobalProperties, currentValue: PossiblyEvaluatedPropertyValue, uniformName: string): void { + setUniform(program: WebGLProgram, uniform: IUniform, globals: GlobalProperties, currentValue: PossiblyEvaluatedPropertyValue, uniformName: string): void { uniform.set(program, uniformName, currentValue.constantOr(this.value)); } // $FlowFixMe[method-unbinding] - getBinding(context: Context, _: string): $Shape> { + getBinding(context: Context, _: string): $Shape> { // $FlowFixMe[method-unbinding] return (this.type === 'color') ? new UniformColor(context) : @@ -131,7 +132,7 @@ class PatternConstantBinder implements UniformBinder { this.pattern = posTo.tl.concat(posTo.br); } - setUniform(program: WebGLProgram, uniform: Uniform<*>, globals: GlobalProperties, currentValue: PossiblyEvaluatedPropertyValue, uniformName: string) { + setUniform(program: WebGLProgram, uniform: IUniform, globals: GlobalProperties, currentValue: PossiblyEvaluatedPropertyValue, uniformName: string) { const pos = uniformName === 'u_pattern' || uniformName === 'u_dash' ? this.pattern : uniformName === 'u_pixel_ratio' ? this.pixelRatio : null; @@ -139,7 +140,7 @@ class PatternConstantBinder implements UniformBinder { } // $FlowFixMe[method-unbinding] - getBinding(context: Context, name: string): $Shape> { + getBinding(context: Context, name: string): $Shape> { // $FlowFixMe[method-unbinding] return name === 'u_pattern' || name === 'u_dash' ? new Uniform4f(context) : @@ -289,7 +290,7 @@ class CompositeExpressionBinder implements AttributeBinder, UniformBinder { } } - setUniform(program: WebGLProgram, uniform: Uniform<*>, globals: GlobalProperties, _: PossiblyEvaluatedPropertyValue<*>, uniformName: string): void { + setUniform(program: WebGLProgram, uniform: IUniform, globals: GlobalProperties, _: PossiblyEvaluatedPropertyValue<*>, uniformName: string): void { const currentZoom = this.useIntegerZoom ? Math.floor(globals.zoom) : globals.zoom; const factor = clamp(this.expression.interpolationFactor(currentZoom, this.zoom, this.zoom + 1), 0, 1); uniform.set(program, uniformName, factor); @@ -339,7 +340,7 @@ class PatternCompositeBinder implements AttributeBinder { const {tl, br, pixelRatio} = pos; for (let i = start; i < end; i++) { - this.paintVertexArray.emplace(i, tl[0], tl[1], br[0], br[1], pixelRatio); + this.paintVertexArray.emplace(i, tl[0], tl[1], br[0], br[1], ((pixelRatio: any): number)); } } @@ -694,7 +695,7 @@ const defaultLayouts = { type LayoutType = 'array' | 'boolean' | 'color' | 'enum' | 'number' | 'resolvedImage' | 'string'; -function layoutType(property: string, type: LayoutType, binderType: string) { +function layoutType(property: string, type: LayoutType, binderType: string): Class { const layoutException = propertyExceptions[property]; // $FlowFixMe[prop-missing] - we don't cover all types in defaultLayouts for some reason return (layoutException && layoutException[binderType]) || defaultLayouts[type][binderType]; diff --git a/src/geo/lng_lat.js b/src/geo/lng_lat.js index 0be5b59c4e8..999ef03aec3 100644 --- a/src/geo/lng_lat.js +++ b/src/geo/lng_lat.js @@ -1,8 +1,33 @@ // @flow +import assert from 'assert'; +import {wrap, degToRad, radToDeg} from '../util/util.js'; +import {GLOBE_RADIUS} from '../geo/projection/globe_constants.js'; -import {wrap} from '../util/util.js'; -import LngLatBounds from './lng_lat_bounds.js'; -import {GLOBE_RADIUS, globeMetersToEcef, latLngToECEF} from '../geo/projection/globe_util.js'; +import type {Vec3} from 'gl-matrix'; + +export function csLatLngToECEF(cosLat: number, sinLat: number, lng: number, radius: number = GLOBE_RADIUS): Vec3 { + lng = degToRad(lng); + + // Convert lat & lng to spherical representation. Use zoom=0 as a reference + const sx = cosLat * Math.sin(lng) * radius; + const sy = -sinLat * radius; + const sz = cosLat * Math.cos(lng) * radius; + + return [sx, sy, sz]; +} + +export function ecefToLatLng([x, y, z]: Array): LngLat { + const radius = Math.hypot(x, y, z); + const lng = Math.atan2(x, z); + const lat = Math.PI * 0.5 - Math.acos(-y / radius); + + return new LngLat(radToDeg(lng), radToDeg(lat)); +} + +export function latLngToECEF(lat: number, lng: number, radius?: number): Vec3 { + assert(lat <= 90 && lat >= -90, 'Lattitude must be between -90 and 90'); + return csLatLngToECEF(Math.cos(degToRad(lat)), Math.sin(degToRad(lat)), lng, radius); +} /* * Approximate radius of the earth in meters. @@ -123,12 +148,12 @@ class LngLat { const latAccuracy = 360 * radius / earthCircumferenceInMetersAtEquator, lngAccuracy = latAccuracy / Math.cos((Math.PI / 180) * this.lat); - return new LngLatBounds(new LngLat(this.lng - lngAccuracy, this.lat - latAccuracy), - new LngLat(this.lng + lngAccuracy, this.lat + latAccuracy)); + return new LngLatBounds({lng: this.lng - lngAccuracy, lat: this.lat - latAccuracy}, + {lng: this.lng + lngAccuracy, lat: this.lat + latAccuracy}); } toEcef(altitude: number): [number, number, number] { - const altInEcef = globeMetersToEcef(altitude); + const altInEcef = altitude * GLOBE_RADIUS / earthRadius; const radius = GLOBE_RADIUS + altInEcef; return (latLngToECEF(this.lat, this.lng, radius): any); } @@ -177,3 +202,319 @@ class LngLat { export type LngLatLike = LngLat | {lng: number, lat: number} | {lon: number, lat: number} | [number, number]; export default LngLat; + +/** + * A `LngLatBounds` object represents a geographical bounding box, + * defined by its southwest and northeast points in longitude and latitude. + * + * If no arguments are provided to the constructor, a `null` bounding box is created. + * + * Note that any Mapbox GL method that accepts a `LngLatBounds` object as an argument or option + * can also accept an `Array` of two {@link LngLatLike} constructs and will perform an implicit conversion. + * This flexible type is documented as {@link LngLatBoundsLike}. + * + * @param {LngLatLike} [sw] The southwest corner of the bounding box. + * @param {LngLatLike} [ne] The northeast corner of the bounding box. + * @example + * const sw = new mapboxgl.LngLat(-73.9876, 40.7661); + * const ne = new mapboxgl.LngLat(-73.9397, 40.8002); + * const llb = new mapboxgl.LngLatBounds(sw, ne); + */ +export class LngLatBounds { + _ne: LngLat; + _sw: LngLat; + + // This constructor is too flexible to type. It should not be so flexible. + constructor(sw: any, ne: any) { + if (!sw) { + // noop + } else if (ne) { + this.setSouthWest(sw).setNorthEast(ne); + } else if (sw.length === 4) { + this.setSouthWest([sw[0], sw[1]]).setNorthEast([sw[2], sw[3]]); + } else { + this.setSouthWest(sw[0]).setNorthEast(sw[1]); + } + } + + /** + * Set the northeast corner of the bounding box. + * + * @param {LngLatLike} ne A {@link LngLatLike} object describing the northeast corner of the bounding box. + * @returns {LngLatBounds} Returns itself to allow for method chaining. + * @example + * const sw = new mapboxgl.LngLat(-73.9876, 40.7661); + * const ne = new mapboxgl.LngLat(-73.9397, 40.8002); + * const llb = new mapboxgl.LngLatBounds(sw, ne); + * llb.setNorthEast([-73.9397, 42.8002]); + */ + setNorthEast(ne: LngLatLike): this { + this._ne = ne instanceof LngLat ? new LngLat(ne.lng, ne.lat) : LngLat.convert(ne); + return this; + } + + /** + * Set the southwest corner of the bounding box. + * + * @param {LngLatLike} sw A {@link LngLatLike} object describing the southwest corner of the bounding box. + * @returns {LngLatBounds} Returns itself to allow for method chaining. + * @example + * const sw = new mapboxgl.LngLat(-73.9876, 40.7661); + * const ne = new mapboxgl.LngLat(-73.9397, 40.8002); + * const llb = new mapboxgl.LngLatBounds(sw, ne); + * llb.setSouthWest([-73.9876, 40.2661]); + */ + setSouthWest(sw: LngLatLike): this { + this._sw = sw instanceof LngLat ? new LngLat(sw.lng, sw.lat) : LngLat.convert(sw); + return this; + } + + /** + * Extend the bounds to include a given LngLatLike or LngLatBoundsLike. + * + * @param {LngLatLike|LngLatBoundsLike} obj Object to extend to. + * @returns {LngLatBounds} Returns itself to allow for method chaining. + * @example + * const sw = new mapboxgl.LngLat(-73.9876, 40.7661); + * const ne = new mapboxgl.LngLat(-73.9397, 40.8002); + * const llb = new mapboxgl.LngLatBounds(sw, ne); + * llb.extend([-72.9876, 42.2661]); + */ + extend(obj: LngLatLike | LngLatBoundsLike): this { + const sw = this._sw, + ne = this._ne; + let sw2, ne2; + + if (obj instanceof LngLat) { + sw2 = obj; + ne2 = obj; + + } else if (obj instanceof LngLatBounds) { + sw2 = obj._sw; + ne2 = obj._ne; + + if (!sw2 || !ne2) return this; + + } else if (Array.isArray(obj)) { + // $FlowFixMe[method-unbinding] + if (obj.length === 4 || obj.every(Array.isArray)) { + const lngLatBoundsObj = ((obj: any): LngLatBoundsLike); + return this.extend(LngLatBounds.convert(lngLatBoundsObj)); + } else { + const lngLatObj = ((obj: any): LngLatLike); + return this.extend(LngLat.convert(lngLatObj)); + } + } else if (typeof obj === 'object' && obj !== null && obj.hasOwnProperty("lat") && (obj.hasOwnProperty("lon") || obj.hasOwnProperty("lng"))) { + return this.extend(LngLat.convert(obj)); + } else { + return this; + } + + if (!sw && !ne) { + this._sw = new LngLat(sw2.lng, sw2.lat); + this._ne = new LngLat(ne2.lng, ne2.lat); + + } else { + sw.lng = Math.min(sw2.lng, sw.lng); + sw.lat = Math.min(sw2.lat, sw.lat); + ne.lng = Math.max(ne2.lng, ne.lng); + ne.lat = Math.max(ne2.lat, ne.lat); + } + + return this; + } + + /** + * Returns the geographical coordinate equidistant from the bounding box's corners. + * + * @returns {LngLat} The bounding box's center. + * @example + * const llb = new mapboxgl.LngLatBounds([-73.9876, 40.7661], [-73.9397, 40.8002]); + * llb.getCenter(); // = LngLat {lng: -73.96365, lat: 40.78315} + */ + getCenter(): LngLat { + return new LngLat((this._sw.lng + this._ne.lng) / 2, (this._sw.lat + this._ne.lat) / 2); + } + + /** + * Returns the southwest corner of the bounding box. + * + * @returns {LngLat} The southwest corner of the bounding box. + * @example + * const llb = new mapboxgl.LngLatBounds([-73.9876, 40.7661], [-73.9397, 40.8002]); + * llb.getSouthWest(); // LngLat {lng: -73.9876, lat: 40.7661} + */ + getSouthWest(): LngLat { return this._sw; } + + /** + * Returns the northeast corner of the bounding box. + * + * @returns {LngLat} The northeast corner of the bounding box. + * @example + * const llb = new mapboxgl.LngLatBounds([-73.9876, 40.7661], [-73.9397, 40.8002]); + * llb.getNorthEast(); // LngLat {lng: -73.9397, lat: 40.8002} + */ + getNorthEast(): LngLat { return this._ne; } + + /** + * Returns the northwest corner of the bounding box. + * + * @returns {LngLat} The northwest corner of the bounding box. + * @example + * const llb = new mapboxgl.LngLatBounds([-73.9876, 40.7661], [-73.9397, 40.8002]); + * llb.getNorthWest(); // LngLat {lng: -73.9876, lat: 40.8002} + */ + getNorthWest(): LngLat { return new LngLat(this.getWest(), this.getNorth()); } + + /** + * Returns the southeast corner of the bounding box. + * + * @returns {LngLat} The southeast corner of the bounding box. + * @example + * const llb = new mapboxgl.LngLatBounds([-73.9876, 40.7661], [-73.9397, 40.8002]); + * llb.getSouthEast(); // LngLat {lng: -73.9397, lat: 40.7661} + */ + getSouthEast(): LngLat { return new LngLat(this.getEast(), this.getSouth()); } + + /** + * Returns the west edge of the bounding box. + * + * @returns {number} The west edge of the bounding box. + * @example + * const llb = new mapboxgl.LngLatBounds([-73.9876, 40.7661], [-73.9397, 40.8002]); + * llb.getWest(); // -73.9876 + */ + getWest(): number { return this._sw.lng; } + + /** + * Returns the south edge of the bounding box. + * + * @returns {number} The south edge of the bounding box. + * @example + * const llb = new mapboxgl.LngLatBounds([-73.9876, 40.7661], [-73.9397, 40.8002]); + * llb.getSouth(); // 40.7661 + */ + getSouth(): number { return this._sw.lat; } + + /** + * Returns the east edge of the bounding box. + * + * @returns {number} The east edge of the bounding box. + * @example + * const llb = new mapboxgl.LngLatBounds([-73.9876, 40.7661], [-73.9397, 40.8002]); + * llb.getEast(); // -73.9397 + */ + getEast(): number { return this._ne.lng; } + + /** + * Returns the north edge of the bounding box. + * + * @returns {number} The north edge of the bounding box. + * @example + * const llb = new mapboxgl.LngLatBounds([-73.9876, 40.7661], [-73.9397, 40.8002]); + * llb.getNorth(); // 40.8002 + */ + getNorth(): number { return this._ne.lat; } + + /** + * Returns the bounding box represented as an array. + * + * @returns {Array>} The bounding box represented as an array, consisting of the + * southwest and northeast coordinates of the bounding represented as arrays of numbers. + * @example + * const llb = new mapboxgl.LngLatBounds([-73.9876, 40.7661], [-73.9397, 40.8002]); + * llb.toArray(); // = [[-73.9876, 40.7661], [-73.9397, 40.8002]] + */ + toArray(): [[number, number], [number, number]] { + return [this._sw.toArray(), this._ne.toArray()]; + } + + /** + * Return the bounding box represented as a string. + * + * @returns {string} The bounding box represents as a string of the format + * `'LngLatBounds(LngLat(lng, lat), LngLat(lng, lat))'`. + * @example + * const llb = new mapboxgl.LngLatBounds([-73.9876, 40.7661], [-73.9397, 40.8002]); + * llb.toString(); // = "LngLatBounds(LngLat(-73.9876, 40.7661), LngLat(-73.9397, 40.8002))" + */ + toString(): string { + return `LngLatBounds(${this._sw.toString()}, ${this._ne.toString()})`; + } + + /** + * Check if the bounding box is an empty/`null`-type box. + * + * @returns {boolean} True if bounds have been defined, otherwise false. + * @example + * const llb = new mapboxgl.LngLatBounds(); + * llb.isEmpty(); // true + * llb.setNorthEast([-73.9876, 40.7661]); + * llb.setSouthWest([-73.9397, 40.8002]); + * llb.isEmpty(); // false + */ + isEmpty(): boolean { + return !(this._sw && this._ne); + } + + /** + * Check if the point is within the bounding box. + * + * @param {LngLatLike} lnglat Geographic point to check against. + * @returns {boolean} True if the point is within the bounding box. + * @example + * const llb = new mapboxgl.LngLatBounds( + * new mapboxgl.LngLat(-73.9876, 40.7661), + * new mapboxgl.LngLat(-73.9397, 40.8002) + * ); + * + * const ll = new mapboxgl.LngLat(-73.9567, 40.7789); + * + * console.log(llb.contains(ll)); // = true + */ + contains(lnglat: LngLatLike): boolean { + const {lng, lat} = LngLat.convert(lnglat); + + const containsLatitude = this._sw.lat <= lat && lat <= this._ne.lat; + let containsLongitude = this._sw.lng <= lng && lng <= this._ne.lng; + if (this._sw.lng > this._ne.lng) { // wrapped coordinates + containsLongitude = this._sw.lng >= lng && lng >= this._ne.lng; + } + + return containsLatitude && containsLongitude; + } + + /** + * Converts an array to a `LngLatBounds` object. + * + * If a `LngLatBounds` object is passed in, the function returns it unchanged. + * + * Internally, the function calls `LngLat#convert` to convert arrays to `LngLat` values. + * + * @param {LngLatBoundsLike} input An array of two coordinates to convert, or a `LngLatBounds` object to return. + * @returns {LngLatBounds} A new `LngLatBounds` object, if a conversion occurred, or the original `LngLatBounds` object. + * @example + * const arr = [[-73.9876, 40.7661], [-73.9397, 40.8002]]; + * const llb = mapboxgl.LngLatBounds.convert(arr); + * console.log(llb); // = LngLatBounds {_sw: LngLat {lng: -73.9876, lat: 40.7661}, _ne: LngLat {lng: -73.9397, lat: 40.8002}} + */ + static convert(input: LngLatBoundsLike): LngLatBounds { + if (!input || input instanceof LngLatBounds) return input; + return new LngLatBounds(input); + } +} + +/** + * A {@link LngLatBounds} object, an array of {@link LngLatLike} objects in [sw, ne] order, + * or an array of numbers in [west, south, east, north] order. + * + * @typedef {LngLatBounds | [LngLatLike, LngLatLike] | [number, number, number, number]} LngLatBoundsLike + * @example + * const v1 = new mapboxgl.LngLatBounds( + * new mapboxgl.LngLat(-73.9876, 40.7661), + * new mapboxgl.LngLat(-73.9397, 40.8002) + * ); + * const v2 = new mapboxgl.LngLatBounds([-73.9876, 40.7661], [-73.9397, 40.8002]); + * const v3 = [[-73.9876, 40.7661], [-73.9397, 40.8002]]; + */ +export type LngLatBoundsLike = LngLatBounds | [LngLatLike, LngLatLike] | [number, number, number, number]; diff --git a/src/geo/lng_lat_bounds.js b/src/geo/lng_lat_bounds.js index a49b8d57221..a5cdccbcb68 100644 --- a/src/geo/lng_lat_bounds.js +++ b/src/geo/lng_lat_bounds.js @@ -221,7 +221,7 @@ class LngLatBounds { * Returns the bounding box represented as an array. * * @returns {Array>} The bounding box represented as an array, consisting of the - * southwest and northeast coordinates of the bounding represented as arrays of numbers. + * southwest and northeast coordinates of the bounding represented as arrays of numbers. * @example * const llb = new mapboxgl.LngLatBounds([-73.9876, 40.7661], [-73.9397, 40.8002]); * llb.toArray(); // = [[-73.9876, 40.7661], [-73.9397, 40.8002]] @@ -234,7 +234,7 @@ class LngLatBounds { * Return the bounding box represented as a string. * * @returns {string} The bounding box represents as a string of the format - * `'LngLatBounds(LngLat(lng, lat), LngLat(lng, lat))'`. + * `'LngLatBounds(LngLat(lng, lat), LngLat(lng, lat))'`. * @example * const llb = new mapboxgl.LngLatBounds([-73.9876, 40.7661], [-73.9397, 40.8002]); * llb.toString(); // = "LngLatBounds(LngLat(-73.9876, 40.7661), LngLat(-73.9397, 40.8002))" diff --git a/src/geo/projection/globe.js b/src/geo/projection/globe.js index c0fc7e6c4c8..cd4e11f70ec 100644 --- a/src/geo/projection/globe.js +++ b/src/geo/projection/globe.js @@ -1,7 +1,7 @@ // @flow import {mat4, vec3} from 'gl-matrix'; import EXTENT from '../../style-spec/data/extent.js'; -import LngLat from '../lng_lat.js'; +import LngLat, {latLngToECEF} from '../lng_lat.js'; import {degToRad} from '../../util/util.js'; import MercatorCoordinate, { mercatorZfromAltitude, @@ -11,8 +11,6 @@ import Point from '@mapbox/point-geometry'; import {farthestPixelDistanceOnPlane, farthestPixelDistanceOnSphere} from './far_z.js'; import {number as interpolate} from '../../style-spec/util/interpolate.js'; import { - GLOBE_SCALE_MATCH_LATITUDE, - latLngToECEF, globeTileBounds, globeNormalizeECEF, globeDenormalizeECEF, @@ -22,6 +20,7 @@ import { tileCoordToECEF, globeMetersToEcef } from './globe_util.js'; +import {GLOBE_SCALE_MATCH_LATITUDE} from './globe_constants.js'; import type Transform from '../transform.js'; import type {ElevationScale} from './projection.js'; diff --git a/src/geo/projection/globe_constants.js b/src/geo/projection/globe_constants.js new file mode 100644 index 00000000000..94a402ad6f9 --- /dev/null +++ b/src/geo/projection/globe_constants.js @@ -0,0 +1,32 @@ +// @flow + +import EXTENT from '../../style-spec/data/extent.js'; + +export const GLOBE_RADIUS = EXTENT / Math.PI / 2.0; + +export const GLOBE_ZOOM_THRESHOLD_MIN = 5; +export const GLOBE_ZOOM_THRESHOLD_MAX = 6; + +// At low zoom levels the globe gets rendered so that the scale at this +// latitude matches it's scale in a mercator map. The choice of latitude is +// a bit arbitrary. Different choices will match mercator more closely in different +// views. 45 is a good enough choice because: +// - it's half way from the pole to the equator +// - matches most middle latitudes reasonably well +// - biases towards increasing size rather than decreasing +// - makes the globe slightly larger at very low zoom levels, where it already +// covers less pixels than mercator (due to the curved surface) +// +// Changing this value will change how large a globe is rendered and could affect +// end users. This should only be done of the tradeoffs between change and improvement +// are carefully considered. +export const GLOBE_SCALE_MATCH_LATITUDE = 45; + +const GLOBE_NORMALIZATION_BIT_RANGE = 15; +export const GLOBE_NORMALIZATION_MASK = (1 << (GLOBE_NORMALIZATION_BIT_RANGE - 1)) - 1; +export const GLOBE_VERTEX_GRID_SIZE = 64; +export const GLOBE_LATITUDINAL_GRID_LOD_TABLE = [GLOBE_VERTEX_GRID_SIZE, GLOBE_VERTEX_GRID_SIZE / 2, GLOBE_VERTEX_GRID_SIZE / 4]; +export const TILE_SIZE = 512; + +export const GLOBE_MIN = -GLOBE_RADIUS; +export const GLOBE_MAX = GLOBE_RADIUS; diff --git a/src/geo/projection/globe_util.js b/src/geo/projection/globe_util.js index 44f228c221e..c8ea5ea8a1f 100644 --- a/src/geo/projection/globe_util.js +++ b/src/geo/projection/globe_util.js @@ -16,8 +16,19 @@ import {members as globeLayoutAttributes} from '../../terrain/globe_attributes.j import posAttributes from '../../data/pos_attributes.js'; import {TriangleIndexArray, GlobeVertexArray, PosArray} from '../../data/array_types.js'; import {Aabb, Ray} from '../../util/primitives.js'; -import LngLat, {earthRadius} from '../lng_lat.js'; -import LngLatBounds from '../lng_lat_bounds.js'; +import LngLat, {earthRadius, csLatLngToECEF, latLngToECEF, LngLatBounds} from '../lng_lat.js'; +import { + GLOBE_RADIUS, + GLOBE_MIN, + GLOBE_MAX, + TILE_SIZE, + GLOBE_NORMALIZATION_MASK, + GLOBE_ZOOM_THRESHOLD_MIN, + GLOBE_ZOOM_THRESHOLD_MAX, + GLOBE_VERTEX_GRID_SIZE, + GLOBE_LATITUDINAL_GRID_LOD_TABLE +} from './globe_constants.js'; +import Point from '@mapbox/point-geometry'; import type Painter from '../../render/painter.js'; import type {CanonicalTileID, UnwrappedTileID} from '../../source/tile_id.js'; @@ -26,36 +37,10 @@ import type {Vec3, Mat4} from 'gl-matrix'; import type IndexBuffer from '../../gl/index_buffer.js'; import type VertexBuffer from '../../gl/vertex_buffer.js'; import type Transform from '../transform.js'; -import Point from '@mapbox/point-geometry'; -import assert from 'assert'; - -export const GLOBE_ZOOM_THRESHOLD_MIN = 5; -export const GLOBE_ZOOM_THRESHOLD_MAX = 6; - -// At low zoom levels the globe gets rendered so that the scale at this -// latitude matches it's scale in a mercator map. The choice of latitude is -// a bit arbitrary. Different choices will match mercator more closely in different -// views. 45 is a good enough choice because: -// - it's half way from the pole to the equator -// - matches most middle latitudes reasonably well -// - biases towards increasing size rather than decreasing -// - makes the globe slightly larger at very low zoom levels, where it already -// covers less pixels than mercator (due to the curved surface) -// -// Changing this value will change how large a globe is rendered and could affect -// end users. This should only be done of the tradeoffs between change and improvement -// are carefully considered. -export const GLOBE_SCALE_MATCH_LATITUDE = 45; - -export const GLOBE_RADIUS = EXTENT / Math.PI / 2.0; -const GLOBE_NORMALIZATION_BIT_RANGE = 15; -const GLOBE_NORMALIZATION_MASK = (1 << (GLOBE_NORMALIZATION_BIT_RANGE - 1)) - 1; -const GLOBE_VERTEX_GRID_SIZE = 64; -const GLOBE_LATITUDINAL_GRID_LOD_TABLE = [GLOBE_VERTEX_GRID_SIZE, GLOBE_VERTEX_GRID_SIZE / 2, GLOBE_VERTEX_GRID_SIZE / 4]; -const TILE_SIZE = 512; - -const GLOBE_MIN = -GLOBE_RADIUS; -const GLOBE_MAX = GLOBE_RADIUS; + +export function globeMetersToEcef(d: number): number { + return d * GLOBE_RADIUS / earthRadius; +} const GLOBE_LOW_ZOOM_TILE_AABBS = [ // z == 0 @@ -67,10 +52,6 @@ const GLOBE_LOW_ZOOM_TILE_AABBS = [ new Aabb([0, 0, GLOBE_MIN], [GLOBE_MAX, GLOBE_MAX, GLOBE_MAX]) // x=1, y=1 ]; -export function globeMetersToEcef(d: number): number { - return d * GLOBE_RADIUS / earthRadius; -} - export function globePointCoordinate(tr: Transform, x: number, y: number, clampToHorizon: boolean = true): ?MercatorCoordinate { const point0 = vec3.scale([], tr._camera.position, tr.worldSize); const point1 = [x, y, 1, 1]; @@ -266,7 +247,7 @@ export function aabbForTileOnGlobe(tr: Transform, numTiles: number, tileId: Cano // It is enough to extend the aabb to contain only the edge that's closest to the center point. const bounds = tileCornersToBounds(tileId, extendToPoles); - const corners = boundsToECEF(bounds); + const corners = boundsToECEF(bounds, GLOBE_RADIUS + globeMetersToEcef(tr._tileCoverLift)); // Transform the corners to world space transformPoints(corners, m, scale); @@ -292,6 +273,15 @@ export function aabbForTileOnGlobe(tr: Transform, numTiles: number, tileId: Cano return new Aabb(cornerMin, cornerMax); } + if (tr._tileCoverLift > 0.0) { + // Early return for elevated globe tiles, where the tile cover optimization is ignored + for (const corner of corners) { + vec3.min(cornerMin, cornerMin, corner); + vec3.max(cornerMax, cornerMax, corner); + } + return new Aabb(cornerMin, cornerMax); + } + // Compute arcs describing edges of the tile on the globe surface. // Vertical edges revolves around the globe origin whereas horizontal edges revolves around the y-axis. const arcCenter = [m[12] * scale, m[13] * scale, m[14] * scale]; @@ -405,7 +395,7 @@ function mercatorTileCornersInCameraSpace({x, y, z}: CanonicalTileID, numTiles: [w, n, 0]]; } -function boundsToECEF(bounds: LngLatBounds): Array { +function boundsToECEF(bounds: LngLatBounds, radius: number = GLOBE_RADIUS): Array { const ny = degToRad(bounds.getNorth()); const sy = degToRad(bounds.getSouth()); const cosN = Math.cos(ny); @@ -415,37 +405,13 @@ function boundsToECEF(bounds: LngLatBounds): Array { const w = bounds.getWest(); const e = bounds.getEast(); return [ - csLatLngToECEF(cosS, sinS, w), - csLatLngToECEF(cosS, sinS, e), - csLatLngToECEF(cosN, sinN, e), - csLatLngToECEF(cosN, sinN, w) + csLatLngToECEF(cosS, sinS, w, radius), + csLatLngToECEF(cosS, sinS, e, radius), + csLatLngToECEF(cosN, sinN, e, radius), + csLatLngToECEF(cosN, sinN, w, radius) ]; } -function csLatLngToECEF(cosLat: number, sinLat: number, lng: number, radius: number = GLOBE_RADIUS): Vec3 { - lng = degToRad(lng); - - // Convert lat & lng to spherical representation. Use zoom=0 as a reference - const sx = cosLat * Math.sin(lng) * radius; - const sy = -sinLat * radius; - const sz = cosLat * Math.cos(lng) * radius; - - return [sx, sy, sz]; -} - -export function ecefToLatLng([x, y, z]: Array): LngLat { - const radius = Math.hypot(x, y, z); - const lng = Math.atan2(x, z); - const lat = Math.PI * 0.5 - Math.acos(-y / radius); - - return new LngLat(radToDeg(lng), radToDeg(lat)); -} - -export function latLngToECEF(lat: number, lng: number, radius?: number): Vec3 { - assert(lat <= 90 && lat >= -90, 'Lattitude must be between -90 and 90'); - return csLatLngToECEF(Math.cos(degToRad(lat)), Math.sin(degToRad(lat)), lng, radius); -} - export function tileCoordToECEF(x: number, y: number, id: CanonicalTileID, radius?: number): Vec3 { const tileCount = 1 << id.z; const mercatorX = (x / EXTENT + id.x) / tileCount; @@ -563,7 +529,7 @@ export function globePoleMatrixForTile(z: number, x: number, tr: Transform): Flo export function globeUseCustomAntiAliasing(painter: Painter, context: Context, transform: Transform): boolean { const transitionT = globeToMercatorTransition(transform.zoom); const useContextAA = painter.style.map._antialias; - const disabled = context.extStandardDerivativesForceOff || (painter.terrain && painter.terrain.exaggeration() > 0.0); + const disabled = context.options.extStandardDerivativesForceOff || (painter.terrain && painter.terrain.exaggeration() > 0.0); return transitionT === 0.0 && !useContextAA && !disabled; } diff --git a/src/geo/projection/tile_transform.js b/src/geo/projection/tile_transform.js index 484b487da86..062529e7063 100644 --- a/src/geo/projection/tile_transform.js +++ b/src/geo/projection/tile_transform.js @@ -9,7 +9,8 @@ import assert from 'assert'; import {CanonicalTileID} from '../../source/tile_id.js'; import type {Vec3} from 'gl-matrix'; -import type Projection, {ProjectedPoint} from './projection.js'; +import type Projection from './projection.js'; +import type {ProjectedPoint} from './projection.js'; import type Transform from '../transform.js'; export type TileTransform = { diff --git a/src/geo/transform.js b/src/geo/transform.js index 23cb0b707c2..b64b9e7b36e 100644 --- a/src/geo/transform.js +++ b/src/geo/transform.js @@ -1,7 +1,6 @@ // @flow -import LngLat from './lng_lat.js'; -import LngLatBounds from './lng_lat_bounds.js'; +import LngLat, {LngLatBounds} from './lng_lat.js'; import MercatorCoordinate, {mercatorXfromLng, mercatorYfromLat, mercatorZfromAltitude, latFromMercatorY, MAX_MERCATOR_LATITUDE, circumferenceAtLatitude} from './mercator_coordinate.js'; import {getProjection} from './projection/index.js'; import {tileAABB} from '../geo/projection/tile_transform.js'; @@ -10,7 +9,7 @@ import {wrap, clamp, pick, radToDeg, degToRad, getAABBPointSquareDist, furthestT import {number as interpolate} from '../style-spec/util/interpolate.js'; import EXTENT from '../style-spec/data/extent.js'; import {vec4, mat4, mat2, vec3, quat} from 'gl-matrix'; -import {Frustum, FrustumCorners, Ray} from '../util/primitives.js'; +import {Frustum, FrustumCorners, Ray, Aabb} from '../util/primitives.js'; import EdgeInsets from './edge_insets.js'; import {FreeCamera, FreeCameraOptions, orientationFromFrame} from '../ui/free_camera.js'; import assert from 'assert'; @@ -18,12 +17,14 @@ import getProjectionAdjustments, {getProjectionAdjustmentInverted, getScaleAdjus import {getPixelsToTileUnitsMatrix} from '../source/pixels_to_tile_units.js'; import {UnwrappedTileID, OverscaledTileID, CanonicalTileID} from '../source/tile_id.js'; import { - calculateGlobeMatrix, - polesInViewport, - aabbForTileOnGlobe, GLOBE_ZOOM_THRESHOLD_MIN, GLOBE_ZOOM_THRESHOLD_MAX, GLOBE_SCALE_MATCH_LATITUDE +} from '../geo/projection/globe_constants.js'; +import { + calculateGlobeMatrix, + polesInViewport, + aabbForTileOnGlobe, } from '../geo/projection/globe_util.js'; import {projectClamped} from '../symbol/projection.js'; @@ -34,7 +35,6 @@ import type Tile from '../source/tile.js'; import type {ProjectionSpecification} from '../style-spec/types.js'; import type {FeatureDistanceData} from '../style-spec/feature_filter/index.js'; import type {Mat4, Vec3, Vec4, Quat} from 'gl-matrix'; -import type {Aabb} from '../util/primitives'; const NUM_WORLD_COPIES = 3; export const DEFAULT_MIN_ZOOM = 0; @@ -113,6 +113,9 @@ class Transform { projMatrix: Array | Float32Array | Float64Array; invProjMatrix: Float64Array; + // Projection matrix with expanded farZ on globe projection + expandedFarZProjMatrix: Array | Float32Array | Float64Array; + // Same as projMatrix, pixel-aligned to avoid fractional pixels for raster tiles alignedProjMatrix: Float64Array; @@ -153,6 +156,7 @@ class Transform { cameraFrustum: Frustum; frustumCorners: FrustumCorners; + _tileCoverLift: number; freezeTileCoverage: boolean; cameraElevationReference: ElevationReference; @@ -177,6 +181,7 @@ class Transform { _projMatrixCache: {[_: number]: Float32Array}; _alignedProjMatrixCache: {[_: number]: Float32Array}; _pixelsToTileUnitsCache: {[_: number]: Float32Array}; + _expandedProjMatrixCache: {[_: number]: Float32Array}; _fogTileMatrixCache: {[_: number]: Float32Array}; _distanceTileDataCache: {[_: number]: FeatureDistanceData}; _camera: FreeCamera; @@ -218,6 +223,7 @@ class Transform { this._projMatrixCache = {}; this._alignedProjMatrixCache = {}; this._fogTileMatrixCache = {}; + this._expandedProjMatrixCache = {}; this._distanceTileDataCache = {}; this._camera = new FreeCamera(); this._centerAltitude = 0; @@ -226,6 +232,8 @@ class Transform { this._pixelsPerMercatorPixel = 1.0; this.globeRadius = 0; this.globeCenterInViewSpace = [0, 0, 0]; + this._tileCoverLift = 0; + this.freezeTileCoverage = false; // Move the horizon closer to the center. 0 would not shift the horizon. 1 would put the horizon at the center. this._horizonShift = 0.1; @@ -252,6 +260,7 @@ class Transform { clone._nearZ = this._nearZ; clone._farZ = this._farZ; clone._averageElevation = this._averageElevation; + clone._orthographicProjectionAtLowPitch = this._orthographicProjectionAtLowPitch; clone._unmodified = this._unmodified; clone._edgeInsets = this._edgeInsets.clone(); clone._camera = this._camera.clone(); @@ -265,7 +274,7 @@ class Transform { return this.projection.name !== 'globe' && this._orthographicProjectionAtLowPitch && this.pitch < OrthographicPitchTranstionValue; } get elevation(): ?Elevation { return this._elevation; } - set elevation(elevation: ?Elevation) { + set elevation(elevation: Elevation | null | void) { if (this._elevation === elevation) return; this._elevation = elevation; this._updateCameraOnTerrain(); @@ -362,7 +371,7 @@ class Transform { get renderWorldCopies(): boolean { return this._renderWorldCopies && this.projection.supportsWorldCopies === true; } - set renderWorldCopies(renderWorldCopies?: ?boolean) { + set renderWorldCopies(renderWorldCopies: ?boolean | void) { if (renderWorldCopies === undefined) { renderWorldCopies = true; } else if (renderWorldCopies === null) { @@ -496,6 +505,12 @@ class Transform { this.zoomFraction = z - this.tileZoom; } + get tileCoverLift(): number { return this._tileCoverLift; } + set tileCoverLift(lift: number) { + if (this._tileCoverLift === lift) return; + this._tileCoverLift = lift; + } + _updateCameraOnTerrain() { const elevationAtCenter = this.elevation ? this.elevation.getAtPoint(this.locationCoordinate(this.center), Number.NEGATIVE_INFINITY) : Number.NEGATIVE_INFINITY; const usePreviousCenter = this.elevation && elevationAtCenter === Number.NEGATIVE_INFINITY && this.elevation.visibleDemTiles.length > 0 && this.elevation.exaggeration() > 0 && @@ -968,7 +983,15 @@ class Transform { // When calculating tile cover for terrain, create deep AABB for nodes, to ensure they intersect frustum: for sources, // other than DEM, use minimum of visible DEM tiles and center altitude as upper bound (pitch is always less than 90°). - const maxRange = options.isTerrainDEM && this._elevation ? this._elevation.exaggeration() * 10000 : this._centerAltitude; + let maxRange; + if (this._elevation && options.isTerrainDEM) { + maxRange = this._elevation.exaggeration() * 10000; + } else if (this._elevation) { + const minMaxOpt = this._elevation.getMinMaxForVisibleTiles(); + maxRange = minMaxOpt ? minMaxOpt.max : this._centerAltitude; + } else { + maxRange = this._centerAltitude; + } const minRange = options.isTerrainDEM ? -maxRange : this._elevation ? this._elevation.getMinElevationBelowMSL() : 0; const scaleAdjustment = this.projection.isReprojectedInTileSpace ? getScaleAdjustment(this) : 1.0; @@ -1851,16 +1874,30 @@ class Transform { * @param {UnwrappedTileID} unwrappedTileID; * @private */ - calculateProjMatrix(unwrappedTileID: UnwrappedTileID, aligned: boolean = false): Float32Array { + calculateProjMatrix(unwrappedTileID: UnwrappedTileID, aligned: boolean = false, expanded: boolean = false): Float32Array { const projMatrixKey = unwrappedTileID.key; - const cache = aligned ? this._alignedProjMatrixCache : this._projMatrixCache; + let cache; + if (expanded) { + cache = this._expandedProjMatrixCache; + } else if (aligned) { + cache = this._alignedProjMatrixCache; + } else { + cache = this._projMatrixCache; + } if (cache[projMatrixKey]) { return cache[projMatrixKey]; } const posMatrix = this.calculatePosMatrix(unwrappedTileID, this.worldSize); - const projMatrix = this.projection.isReprojectedInTileSpace ? - this.mercatorMatrix : (aligned ? this.alignedProjMatrix : this.projMatrix); + let projMatrix; + if (this.projection.isReprojectedInTileSpace) { + projMatrix = this.mercatorMatrix; + } else if (expanded) { + assert(!aligned); + projMatrix = this.expandedFarZProjMatrix; + } else { + projMatrix = aligned ? this.alignedProjMatrix : this.projMatrix; + } mat4.multiply(posMatrix, projMatrix, posMatrix); cache[projMatrixKey] = new Float32Array(posMatrix); @@ -2153,6 +2190,15 @@ class Transform { // as tile elevations are in tile coordinates and relative to center elevation. this.invProjMatrix = mat4.invert(new Float64Array(16), this.projMatrix); + if (isGlobe) { + const expandedCameraToClipPerspective = this._camera.getCameraToClipPerspective(this._fov, this.width / this.height, this._nearZ, Infinity); + expandedCameraToClipPerspective[8] = -offset.x * 2 / this.width; + expandedCameraToClipPerspective[9] = offset.y * 2 / this.height; + this.expandedFarZProjMatrix = mat4.mul([], expandedCameraToClipPerspective, worldToCamera); + } else { + this.expandedFarZProjMatrix = this.projMatrix; + } + const clipToCamera = mat4.invert([], cameraToClip); this.frustumCorners = FrustumCorners.fromInvProjectionMatrix(clipToCamera, this.horizonLineFromTop(), this.height); @@ -2228,6 +2274,7 @@ class Transform { this._projMatrixCache = {}; this._alignedProjMatrixCache = {}; this._pixelsToTileUnitsCache = {}; + this._expandedProjMatrixCache = {}; } _calcFogMatrices() { @@ -2352,7 +2399,7 @@ class Transform { // drape raster overscale artifacts or cut terrain (see under it) as it gets clipped on // near plane. Returned value is in mercator coordinates. const MAX_DRAPE_OVERZOOM = 4; - const zoom = Math.min((this._seaLevelZoom != null ? this._seaLevelZoom : this._zoom) + MAX_DRAPE_OVERZOOM, this._maxZoom); + const zoom = Math.min((this._seaLevelZoom != null ? this._seaLevelZoom : this._zoom), this._maxZoom) + MAX_DRAPE_OVERZOOM; return this._mercatorZfromZoom(zoom); } diff --git a/src/gl/context.js b/src/gl/context.js index 9ee4756d897..438e3b1e9c2 100644 --- a/src/gl/context.js +++ b/src/gl/context.js @@ -25,6 +25,12 @@ type ClearArgs = { colorMask?: ColorMaskType }; +export type ContextOptions = { + extTextureFilterAnisotropicForceOff?: boolean; + extTextureFloatLinearForceOff?: boolean; + extStandardDerivativesForceOff?: boolean; +}; + class Context { gl: WebGL2RenderingContext; currentNumAttributes: ?number; @@ -71,11 +77,10 @@ class Context { extDebugRendererInfo: any; extTimerQuery: any; extTextureFloatLinear: any; + options: ContextOptions; + maxPointSize: number; - extTextureFilterAnisotropicForceOff: boolean; - extStandardDerivativesForceOff: boolean; - - constructor(gl: WebGL2RenderingContext) { + constructor(gl: WebGL2RenderingContext, options?: ContextOptions) { this.gl = gl; this.clearColor = new ClearColor(this); @@ -109,17 +114,18 @@ class Context { this.pixelStoreUnpack = new PixelStoreUnpack(this); this.pixelStoreUnpackPremultiplyAlpha = new PixelStoreUnpackPremultiplyAlpha(this); this.pixelStoreUnpackFlipY = new PixelStoreUnpackFlipY(this); + this.options = options ? {...options} : {}; - this.extTextureFilterAnisotropic = ( - gl.getExtension('EXT_texture_filter_anisotropic') || + if (!this.options.extTextureFilterAnisotropicForceOff) { + this.extTextureFilterAnisotropic = ( + gl.getExtension('EXT_texture_filter_anisotropic') || gl.getExtension('MOZ_EXT_texture_filter_anisotropic') || gl.getExtension('WEBKIT_EXT_texture_filter_anisotropic') - ); - if (this.extTextureFilterAnisotropic) { - this.extTextureFilterAnisotropicMax = gl.getParameter(this.extTextureFilterAnisotropic.MAX_TEXTURE_MAX_ANISOTROPY_EXT); + ); + if (this.extTextureFilterAnisotropic) { + this.extTextureFilterAnisotropicMax = gl.getParameter(this.extTextureFilterAnisotropic.MAX_TEXTURE_MAX_ANISOTROPY_EXT); + } } - this.extTextureFilterAnisotropicForceOff = false; - this.extStandardDerivativesForceOff = false; this.extDebugRendererInfo = gl.getExtension('WEBGL_debug_renderer_info'); if (this.extDebugRendererInfo) { @@ -127,11 +133,14 @@ class Context { this.vendor = gl.getParameter(this.extDebugRendererInfo.UNMASKED_VENDOR_WEBGL); } - this.extTextureFloatLinear = gl.getExtension('OES_texture_float_linear'); + if (!this.options.extTextureFloatLinearForceOff) { + this.extTextureFloatLinear = gl.getExtension('OES_texture_float_linear'); + } this.extRenderToTextureHalfFloat = gl.getExtension('EXT_color_buffer_half_float'); this.extTimerQuery = gl.getExtension('EXT_disjoint_timer_query_webgl2'); this.maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE); + this.maxPointSize = gl.getParameter(gl.ALIASED_POINT_SIZE_RANGE)[1]; } setDefault() { diff --git a/src/gl/types.js b/src/gl/types.js index bde2a7a40ed..2d5ba049ccc 100644 --- a/src/gl/types.js +++ b/src/gl/types.js @@ -22,7 +22,9 @@ export type BlendFuncType = [BlendFuncConstant, BlendFuncConstant, BlendFuncCons export type BlendEquationType = | $PropertyType | $PropertyType - | $PropertyType; + | $PropertyType + | $PropertyType + | $PropertyType; export type ColorMaskType = [boolean, boolean, boolean, boolean]; diff --git a/src/index.js b/src/index.js index 233e65e97ac..32b5621f121 100644 --- a/src/index.js +++ b/src/index.js @@ -6,7 +6,7 @@ import assert from 'assert'; import {supported} from '@mapbox/mapbox-gl-supported'; import {version} from '../package.json'; -import Map from './ui/map.js'; +import {Map} from './ui/map.js'; import NavigationControl from './ui/control/navigation_control.js'; import GeolocateControl from './ui/control/geolocate_control.js'; import AttributionControl from './ui/control/attribution_control.js'; @@ -15,8 +15,7 @@ import FullscreenControl from './ui/control/fullscreen_control.js'; import Popup from './ui/popup.js'; import Marker from './ui/marker.js'; import Style from './style/style.js'; -import LngLat from './geo/lng_lat.js'; -import LngLatBounds from './geo/lng_lat_bounds.js'; +import LngLat, {LngLatBounds} from './geo/lng_lat.js'; import Point from '@mapbox/point-geometry'; import MercatorCoordinate from './geo/mercator_coordinate.js'; import {Evented} from './util/evented.js'; @@ -25,11 +24,12 @@ import {Debug} from './util/debug.js'; import {isSafari} from './util/util.js'; import {setRTLTextPlugin, getRTLTextPluginStatus} from './source/rtl_text_plugin.js'; import WorkerPool from './util/worker_pool.js'; +import WorkerClass from './util/worker_class.js'; import {prewarm, clearPrewarmedResources} from './util/global_worker_pool.js'; import {clearTileCache} from './util/tile_request_cache.js'; import {WorkerPerformanceUtils} from './util/worker_performance_utils.js'; import {FreeCameraOptions} from './ui/free_camera.js'; -import {getDracoUrl, setDracoUrl} from '../3d-style/util/loaders.js'; +import {getDracoUrl, setDracoUrl, setMeshoptUrl, getMeshoptUrl} from '../3d-style/util/loaders.js'; import browser from './util/browser.js'; const exported = { @@ -195,7 +195,13 @@ const exported = { * ... * */ - workerUrl: '', + get workerUrl(): string { + return WorkerClass.workerUrl; + }, + + set workerUrl(url: string) { + WorkerClass.workerUrl = url; + }, /** * Provides an interface for external module bundlers such as Webpack or Rollup to package @@ -211,7 +217,21 @@ const exported = { * * mapboxgl.workerClass = MapboxGLWorker; */ - workerClass: null, + get workerClass(): Object { + return WorkerClass.workerClass; + }, + + set workerClass(klass: Object) { + WorkerClass.workerClass = klass; + }, + + get workerParams(): Object { + return WorkerClass.workerParams; + }, + + set workerParams(params: Object) { + WorkerClass.workerParams = params; + }, /** * Provides an interface for loading Draco decoding library (draco_decoder_gltf.wasm v1.5.6) from a self-hosted URL. @@ -238,6 +258,14 @@ const exported = { setDracoUrl(url); }, + get meshoptUrl(): string { + return getMeshoptUrl(); + }, + + set meshoptUrl(url: string) { + setMeshoptUrl(url); + }, + /** * Sets the time used by Mapbox GL JS internally for all animations. Useful for generating videos from Mapbox GL JS. * @@ -269,9 +297,9 @@ Debug.extend(exported, {isSafari, getPerformanceMetrics: PerformanceUtils.getPer * @function supported * @param {Object} [options] * @param {boolean} [options.failIfMajorPerformanceCaveat=false] If `true`, - * the function will return `false` if the performance of Mapbox GL JS would - * be dramatically worse than expected (for example, a software WebGL renderer - * would be used). + * the function will return `false` if the performance of Mapbox GL JS would + * be dramatically worse than expected (for example, a software WebGL renderer + * would be used). * @return {boolean} * @example * // Show an alert if the browser does not support Mapbox GL @@ -289,7 +317,7 @@ Debug.extend(exported, {isSafari, getPerformanceMetrics: PerformanceUtils.getPer * @param {string} pluginURL URL pointing to the Mapbox RTL text plugin source. * @param {Function} callback Called with an error argument if there is an error, or no arguments if the plugin loads successfully. * @param {boolean} lazy If set to `true`, MapboxGL will defer loading the plugin until right-to-left text is encountered, and - * right-to-left text will be rendered only after the plugin finishes loading. + * right-to-left text will be rendered only after the plugin finishes loading. * @example * mapboxgl.setRTLTextPlugin('https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-rtl-text/v0.2.0/mapbox-gl-rtl-text.js'); * @see [Example: Add support for right-to-left scripts](https://www.mapbox.com/mapbox-gl-js/example/mapbox-gl-rtl-text/) diff --git a/src/render/cutoff.js b/src/render/cutoff.js index c2bc869a95d..4cc6fb6892f 100644 --- a/src/render/cutoff.js +++ b/src/render/cutoff.js @@ -2,11 +2,12 @@ import Context from '../gl/context.js'; import type {UniformValues} from './uniform_binding.js'; -import Painter from './painter.js'; import {Uniform4f} from './uniform_binding.js'; import {smoothstep, warnOnce} from '../util/util.js'; import {MIN_LOD_PITCH} from '../geo/transform.js'; +import type Painter from './painter.js'; + export type CutoffUniformsType = {| 'u_cutoff_params': Uniform4f, |}; @@ -42,7 +43,7 @@ export const getCutoffParams = ( return { shouldRenderCutoff: false, uniformValues: { - 'u_cutoff_params': [0, 0, 0, 0] + 'u_cutoff_params': [0, 0, 0, 1] } }; } @@ -54,8 +55,7 @@ export const getCutoffParams = ( const zRange = tr._farZ - tr._nearZ; const cameraToCenterDistance = tr.cameraToCenterDistance; const fadeRangePixels = cutoffFadeRange * tr.height; - // Scaled down by 0.75 because at the minimum zoom level the first LOD tile closer than the center - const cutoffDistance = lerp(cameraToCenterDistance * 0.75, tr._farZ + fadeRangePixels, pitchScale) * zoomScale; + const cutoffDistance = lerp(cameraToCenterDistance, tr._farZ + fadeRangePixels, pitchScale) * zoomScale; const relativeCutoffDistance = ((cutoffDistance - tr._nearZ) / zRange); const relativeCutoffFadeDistance = ((cutoffDistance - fadeRangePixels - tr._nearZ) / zRange); return { diff --git a/src/render/draw_atmosphere.js b/src/render/draw_atmosphere.js index 9224895e14d..0b01bbfd43d 100644 --- a/src/render/draw_atmosphere.js +++ b/src/render/draw_atmosphere.js @@ -10,19 +10,19 @@ import { globeUseCustomAntiAliasing } from './../geo/projection/globe_util.js'; import {atmosphereUniformValues} from '../terrain/globe_raster_program.js'; -import type Painter from './painter.js'; import {AtmosphereBuffer} from '../render/atmosphere_buffer.js'; import {degToRad, mapValue, clamp} from '../util/util.js'; import {mat3, vec3, mat4, quat} from 'gl-matrix'; -import type {Vec3} from 'gl-matrix'; import Fog from '../style/fog.js'; -import type IndexBuffer from '../gl/index_buffer.js'; -import type VertexBuffer from '../gl/vertex_buffer.js'; import SegmentVector from '../data/segment.js'; import {TriangleIndexArray, StarsVertexArray} from '../data/array_types.js'; import {starsLayout} from './stars_attributes.js'; import {starsUniformValues} from '../terrain/stars_program.js'; import {mulberry32} from '../style-spec/util/random.js'; +import type Painter from './painter.js'; +import type {Vec3} from 'gl-matrix'; +import type IndexBuffer from '../gl/index_buffer.js'; +import type VertexBuffer from '../gl/vertex_buffer.js'; import type {DynamicDefinesType} from './program/program_uniforms.js'; function generateUniformDistributedPointsOnSphere(pointsCount: number): Array { @@ -39,6 +39,19 @@ function generateUniformDistributedPointsOnSphere(pointsCount: number): Array { this.updateNeeded = true; }); + painter.tp.registerParameter(this.params, ["Stars"], "sizeMultiplier", {min:0.01, max: 2.0, step:0.01}); + painter.tp.registerParameter(this.params, ["Stars"], "sizeRange", {min:0.0, max: 200.0, step:1}, () => { this.updateNeeded = true; }); + painter.tp.registerParameter(this.params, ["Stars"], "intensityRange", {min:0.0, max: 200.0, step:1}, () => { this.updateNeeded = true; }); } update(painter: Painter) { const context = painter.context; - if (!this.atmosphereBuffer) { + if (!this.atmosphereBuffer || this.updateNeeded) { + this.updateNeeded = false; + this.atmosphereBuffer = new AtmosphereBuffer(context); // Part of internal stlye spec, not exposed to gl-js - const starsCount = 16000; - const sizeRange = 100.0; - const intensityRange = 200.0; + const sizeRange = this.params.sizeRange; + const intensityRange = this.params.intensityRange; - const stars = generateUniformDistributedPointsOnSphere(starsCount); + const stars = generateUniformDistributedPointsOnSphere(this.params.starsCount); const sRand = mulberry32(300); const vertices = new StarsVertexArray(); @@ -188,9 +213,6 @@ class Atmosphere { const program = painter.getOrCreateProgram('stars'); - // Exposed in internal style spec for mobile - const sizeMultiplier = 0.15; - const orientation = quat.identity([]); quat.rotateX(orientation, orientation, -tr._pitch); @@ -208,10 +230,10 @@ class Atmosphere { const camUp = [0, 1, 0]; vec3.transformMat3(camUp, camUp, modelviewInv); - vec3.scale(camUp, camUp, sizeMultiplier); + vec3.scale(camUp, camUp, this.params.sizeMultiplier); const camRight = [1, 0, 0]; vec3.transformMat3(camRight, camRight, modelviewInv); - vec3.scale(camRight, camRight, sizeMultiplier); + vec3.scale(camRight, camRight, this.params.sizeMultiplier); const uniforms = starsUniformValues( mvp, diff --git a/src/render/draw_collision_debug.js b/src/render/draw_collision_debug.js index 8c2cdfd1524..e858ec50de1 100644 --- a/src/render/draw_collision_debug.js +++ b/src/render/draw_collision_debug.js @@ -1,10 +1,5 @@ // @flow -import type Painter from './painter.js'; -import type SourceCache from '../source/source_cache.js'; -import type StyleLayer from '../style/style_layer.js'; -import type {OverscaledTileID} from '../source/tile_id.js'; -import type SymbolBucket from '../data/bucket/symbol_bucket.js'; import DepthMode from '../gl/depth_mode.js'; import StencilMode from '../gl/stencil_mode.js'; import CullFaceMode from '../gl/cull_face_mode.js'; @@ -13,11 +8,17 @@ import {QuadTriangleArray, CollisionCircleLayoutArray} from '../data/array_types import {collisionCircleLayout} from '../data/bucket/symbol_attributes.js'; import SegmentVector from '../data/segment.js'; import {mat4} from 'gl-matrix'; -import type Projection from '../geo/projection/projection.js'; import {getCollisionDebugTileProjectionMatrix} from '../geo/projection/projection_util.js'; import VertexBuffer from '../gl/vertex_buffer.js'; import IndexBuffer from '../gl/index_buffer.js'; +import type Painter from './painter.js'; +import type SourceCache from '../source/source_cache.js'; +import type StyleLayer from '../style/style_layer.js'; +import type {OverscaledTileID} from '../source/tile_id.js'; +import type SymbolBucket from '../data/bucket/symbol_bucket.js'; +import type Projection from '../geo/projection/projection.js'; + export default drawCollisionDebug; type TileBatch = { diff --git a/src/render/draw_custom.js b/src/render/draw_custom.js index bdec2574746..e0df30a9b2b 100644 --- a/src/render/draw_custom.js +++ b/src/render/draw_custom.js @@ -20,7 +20,7 @@ function drawCustom(painter: Painter, sourceCache: SourceCache, layer: CustomSty const implementation = layer.implementation; if (painter.transform.projection.unsupportedLayers && painter.transform.projection.unsupportedLayers.includes("custom") && - !(painter.terrain && (painter.terrain.renderingToTexture || painter.renderPass === 'offscreen') && layer.isLayerDraped(sourceCache))) { + !(painter.terrain && (painter.terrain.renderingToTexture || painter.renderPass === 'offscreen') && layer.isDraped(sourceCache))) { warnOnce('Custom layers are not yet supported with this projection. Use mercator or globe to enable usage of custom layers.'); return; } diff --git a/src/render/draw_debug.js b/src/render/draw_debug.js index 1e097b81eb5..1595f918d32 100644 --- a/src/render/draw_debug.js +++ b/src/render/draw_debug.js @@ -20,9 +20,20 @@ const leftColor = new Color(0, 0, 1, 1); const rightColor = new Color(1, 0, 1, 1); const centerColor = new Color(0, 1, 1, 1); -export default function drawDebug(painter: Painter, sourceCache: SourceCache, coords: Array) { +export default function drawDebug(painter: Painter, sourceCache: SourceCache, coords: Array, color: Color, silhouette: boolean, showParseStatus: boolean) { for (let i = 0; i < coords.length; i++) { - drawDebugTile(painter, sourceCache, coords[i]); + if (silhouette) { + const radius = 1; + const darkenFactor = 0.8; + const colorMiddle = new Color(color.r * darkenFactor, color.g * darkenFactor, color.b * darkenFactor, 1.0); + drawDebugTile(painter, sourceCache, coords[i], color, -radius, -radius, showParseStatus); + drawDebugTile(painter, sourceCache, coords[i], color, -radius, radius, showParseStatus); + drawDebugTile(painter, sourceCache, coords[i], color, radius, radius, showParseStatus); + drawDebugTile(painter, sourceCache, coords[i], color, radius, -radius, showParseStatus); + drawDebugTile(painter, sourceCache, coords[i], colorMiddle, 0, 0, showParseStatus); + } else { + drawDebugTile(painter, sourceCache, coords[i], color, 0, 0, showParseStatus); + } } } @@ -48,7 +59,7 @@ export function drawDebugQueryGeometry(painter: Painter, sourceCache: SourceCach } } -function drawDebugTile(painter: Painter, sourceCache: SourceCache, coord: OverscaledTileID) { +function drawDebugTile(painter: Painter, sourceCache: SourceCache, coord: OverscaledTileID, color: Color, offsetX: number, offsetY: number, showParseStatus: boolean) { const context = painter.context; const tr = painter.transform; const gl = context.gl; @@ -56,7 +67,7 @@ function drawDebugTile(painter: Painter, sourceCache: SourceCache, coord: Oversc const isGlobeProjection = tr.projection.name === 'globe'; const definesValues = isGlobeProjection ? ['PROJECTION_GLOBE_VIEW'] : []; - let posMatrix = coord.projMatrix; + let posMatrix = mat4.clone(coord.projMatrix); if (isGlobeProjection && globeToMercatorTransition(tr.zoom) > 0) { // We use a custom tile matrix here in order to handle the globe-to-mercator transition @@ -65,9 +76,16 @@ function drawDebugTile(painter: Painter, sourceCache: SourceCache, coord: Oversc const bounds = transitionTileAABBinECEF(coord.canonical, tr); const decode = globeDenormalizeECEF(bounds); posMatrix = mat4.multiply(new Float32Array(16), tr.globeMatrix, decode); + mat4.multiply(posMatrix, tr.projMatrix, posMatrix); } + const jitterMatrix = mat4.create(); + jitterMatrix[12] += 2 * offsetX / (browser.devicePixelRatio * tr.width); + jitterMatrix[13] += 2 * offsetY / (browser.devicePixelRatio * tr.height); + + mat4.multiply(posMatrix, jitterMatrix, posMatrix); + const program = painter.getOrCreateProgram('debug', {defines: definesValues}); const tile = sourceCache.getTileByID(coord.key); if (painter.terrain) painter.terrain.setupElevationDraw(tile, program); @@ -92,22 +110,25 @@ function drawDebugTile(painter: Painter, sourceCache: SourceCache, coord: Oversc const debugSegments = tile._tileDebugSegments || painter.debugSegments; program.draw(painter, gl.LINE_STRIP, depthMode, stencilMode, colorMode, CullFaceMode.disabled, - debugUniformValues(posMatrix, Color.red), id, + debugUniformValues(posMatrix, color), id, debugBuffer, debugIndexBuffer, debugSegments, null, null, null, [tile._globeTileDebugBorderBuffer]); - const tileRawData = tile.latestRawTileData; - const tileByteLength = (tileRawData && tileRawData.byteLength) || 0; - const tileSizeKb = Math.floor(tileByteLength / 1024); - const tileSize = sourceCache.getTile(coord).tileSize; - const scaleRatio = (512 / Math.min(tileSize, 512) * (coord.overscaledZ / tr.zoom)) * 0.5; - let tileLabel = coord.canonical.toString(); - if (coord.overscaledZ !== coord.canonical.z) { - tileLabel += ` => ${coord.overscaledZ}`; + if (showParseStatus) { + const tileRawData = tile.latestRawTileData; + const tileByteLength = (tileRawData && tileRawData.byteLength) || 0; + const tileSizeKb = Math.floor(tileByteLength / 1024); + let tileLabel = coord.canonical.toString(); + if (coord.overscaledZ !== coord.canonical.z) { + tileLabel += ` => ${coord.overscaledZ}`; + } + tileLabel += ` ${tile.state}`; + tileLabel += ` ${tileSizeKb}kb`; + drawTextToOverlay(painter, tileLabel); } - tileLabel += ` ${tileSizeKb}kb`; - drawTextToOverlay(painter, tileLabel); + const tileSize = sourceCache.getTile(coord).tileSize; + const scaleRatio = (512 / Math.min(tileSize, 512) * (coord.overscaledZ / tr.zoom)) * 0.5; const debugTextBuffer = tile._tileDebugTextBuffer || painter.debugBuffer; const debugTextIndexBuffer = tile._tileDebugTextIndexBuffer || painter.quadTriangleIndexBuffer; const debugTextSegments = tile._tileDebugTextSegments || painter.debugSegments; diff --git a/src/render/draw_fill_extrusion.js b/src/render/draw_fill_extrusion.js index 16349a54b8c..cad6ae09c18 100644 --- a/src/render/draw_fill_extrusion.js +++ b/src/render/draw_fill_extrusion.js @@ -50,7 +50,6 @@ function draw(painter: Painter, source: SourceCache, layer: FillExtrusionStyleLa const gl = context.gl; const terrain = painter.terrain; const rtt = terrain && terrain.renderingToTexture; - const cutoffFadeRange = layer.paint.get('fill-extrusion-cutoff-fade-range'); if (opacity === 0) { return; } @@ -93,11 +92,12 @@ function draw(painter: Painter, source: SourceCache, layer: FillExtrusionStyleLa } else if (painter.renderPass === 'translucent') { const noPattern = !layer.paint.get('fill-extrusion-pattern').constantOr((1: any)); + const color = layer.paint.get('fill-extrusion-color').constantOr(Color.white); - if (!rtt) { + if (!rtt && color.a !== 0.0) { const depthMode = new DepthMode(painter.context.gl.LEQUAL, DepthMode.ReadWrite, painter.depthRangeFor3D); - if (cutoffFadeRange === 0.0 && opacity === 1 && noPattern) { + if (opacity === 1 && noPattern) { drawExtrusionTiles(painter, source, layer, coords, depthMode, StencilMode.disabled, ColorMode.unblended, conflateLayer); } else { // Draw transparent buildings in two passes so that only the closest surface is drawn. @@ -149,7 +149,6 @@ function draw(painter: Painter, source: SourceCache, layer: FillExtrusionStyleLa // Mark the alpha channel with the DF values (that determine the intensity of the effects). No color is written. /* $FlowFixMe[incompatible-call] */ const stencilSdfPass = new StencilMode({func: gl.ALWAYS, mask: 0xFF}, 0xFF, 0xFF, gl.KEEP, gl.KEEP, gl.REPLACE); - /* $FlowFixMe[prop-missing] */ const colorSdfPass = new ColorMode([gl.ONE, gl.ONE, gl.ONE, gl.ONE], Color.transparent, [false, false, false, true], gl.MIN); drawGroundEffect(painter, source, layer, coords, depthMode, stencilSdfPass, colorSdfPass, CullFaceMode.disabled, aoPass, 'sdf', opacity, aoIntensity, aoRadius, floodLightIntensity, floodLightColor, attenuation, conflateLayer, false); } @@ -180,7 +179,6 @@ function draw(painter: Painter, source: SourceCache, layer: FillExtrusionStyleLa // Mark the alpha channel with the DF values (that determine the intensity of the effects). No color is written. /* $FlowFixMe[incompatible-call] */ const stencilSdfPass = new StencilMode({func: gl.ALWAYS, mask: 0xFF}, 0xFF, 0xFF, gl.KEEP, gl.KEEP, gl.REPLACE); - /* $FlowFixMe[prop-missing] */ const colorSdfPass = new ColorMode([gl.ONE, gl.ONE, gl.ONE, gl.ONE], Color.transparent, [false, false, false, true], gl.MIN); drawGroundEffect(painter, source, layer, coords, depthMode, stencilSdfPass, colorSdfPass, CullFaceMode.disabled, aoPass, 'sdf', opacity, aoIntensity, aoRadius, floodLightIntensity, floodLightColor, attenuation, conflateLayer, renderNeighbors); } @@ -199,7 +197,6 @@ function draw(painter: Painter, source: SourceCache, layer: FillExtrusionStyleLa // We don't really need to encode the alpha values for AO as the layers have already been multiplied by its intensity. The gl.FUNC_ADD (as blending equation) and gl.ZERO (as dest alpha factor) would ensure this. const dstAlphaFactor = aoPass ? gl.ZERO : gl.ONE; - /* $FlowFixMe[prop-missing] */ const blendEquation = aoPass ? gl.FUNC_ADD : gl.MAX; const colorMode = new ColorMode([gl.ONE, gl.ONE, gl.ONE, dstAlphaFactor], Color.transparent, [false, false, false, true], blendEquation); drawGroundEffect(painter, source, layer, coords, depthMode, StencilMode.disabled, colorMode, CullFaceMode.disabled, aoPass, 'clear', opacity, aoIntensity, aoRadius, floodLightIntensity, floodLightColor, attenuation, conflateLayer, renderNeighbors, framebufferCopyTexture); @@ -246,6 +243,7 @@ function draw(painter: Painter, source: SourceCache, layer: FillExtrusionStyleLa } function drawExtrusionTiles(painter: Painter, source: SourceCache, layer: FillExtrusionStyleLayer, coords: Array, depthMode: DepthMode, stencilMode: StencilMode, colorMode: ColorMode, replacementActive: boolean) { + layer.resetLayerRenderingStats(painter); const context = painter.context; const gl = context.gl; const tr = painter.transform; @@ -266,6 +264,7 @@ function drawExtrusionTiles(painter: Painter, source: SourceCache, layer: FillEx const floodLightIntensity = layer.paint.get('fill-extrusion-flood-light-intensity'); const verticalScale = layer.paint.get('fill-extrusion-vertical-scale'); const cutoffParams = getCutoffParams(painter, layer.paint.get('fill-extrusion-cutoff-fade-range')); + const emissiveStrength = layer.paint.get('fill-extrusion-emissive-strength'); const baseDefines = ([]: any); if (isGlobeProjection) { baseDefines.push('PROJECTION_GLOBE_VIEW'); @@ -305,6 +304,7 @@ function drawExtrusionTiles(painter: Painter, source: SourceCache, layer: FillEx } const programName = drawDepth ? 'fillExtrusionDepth' : (image ? 'fillExtrusionPattern' : 'fillExtrusion'); + const stats = layer.getLayerRenderingStats(); for (const coord of coords) { const tile = source.getTile(coord); const bucket: ?FillExtrusionBucket = (tile.getBucket(layer): any); @@ -322,11 +322,7 @@ function drawExtrusionTiles(painter: Painter, source: SourceCache, layer: FillEx if (painter.terrain) { const terrain = painter.terrain; - if (painter.style.terrainSetForDrapingOnly()) { - terrain.setupElevationDraw(tile, program, {useMeterToDem: true}); - } else { - terrain.setupElevationDraw(tile, program, {useMeterToDem: true}); - } + terrain.setupElevationDraw(tile, program, {useMeterToDem: true}); } if (!bucket.centroidVertexBuffer) { @@ -362,7 +358,7 @@ function drawExtrusionTiles(painter: Painter, source: SourceCache, layer: FillEx uniformValues = fillExtrusionDepthUniformValues(tileMatrix, roofEdgeRadius, verticalScale); } else { const matrix = painter.translatePosMatrix( - coord.projMatrix, + coord.expandedProjMatrix, tile, layer.paint.get('fill-extrusion-translate'), layer.paint.get('fill-extrusion-translate-anchor')); @@ -373,9 +369,8 @@ function drawExtrusionTiles(painter: Painter, source: SourceCache, layer: FillEx uniformValues = fillExtrusionPatternUniformValues(matrix, painter, shouldUseVerticalGradient, opacity, ao, roofEdgeRadius, coord, tile, heightLift, globeToMercator, mercatorCenter, invMatrix, floodLightColor, verticalScale); } else { - uniformValues = fillExtrusionUniformValues(matrix, painter, shouldUseVerticalGradient, opacity, ao, roofEdgeRadius, coord, - heightLift, globeToMercator, mercatorCenter, invMatrix, floodLightColor, verticalScale, floodLightIntensity, groundShadowFactor); + heightLift, globeToMercator, mercatorCenter, invMatrix, floodLightColor, verticalScale, floodLightIntensity, groundShadowFactor, emissiveStrength); } } @@ -383,15 +378,34 @@ function drawExtrusionTiles(painter: Painter, source: SourceCache, layer: FillEx assert(!isGlobeProjection || bucket.layoutVertexExtBuffer); + let segments = bucket.segments; + if (tr.projection.name === 'mercator' && !isShadowPass) { + segments = bucket.getVisibleSegments(tile.tileID, painter.terrain, painter.transform.getFrustum(0)); + if (!segments.get().length) { + continue; + } + } + if (stats) { + if (!isShadowPass) { + for (const segment of segments.get()) { + stats.numRenderedVerticesInTransparentPass += segment.primitiveLength; + } + } else { + for (const segment of segments.get()) { + stats.numRenderedVerticesInShadowPass += segment.primitiveLength; + } + } + } const dynamicBuffers = []; if (painter.terrain || replacementActive) dynamicBuffers.push(bucket.centroidVertexBuffer); if (isGlobeProjection) dynamicBuffers.push(bucket.layoutVertexExtBuffer); program.draw(painter, context.gl.TRIANGLES, depthMode, stencilMode, colorMode, CullFaceMode.backCCW, uniformValues, layer.id, bucket.layoutVertexBuffer, bucket.indexBuffer, - bucket.segments, layer.paint, painter.transform.zoom, + segments, layer.paint, painter.transform.zoom, programConfiguration, dynamicBuffers); } + if (painter.shadowRenderer) painter.shadowRenderer.useNormalOffset = false; } @@ -712,10 +726,11 @@ function updateBorders(context: Context, source: SourceCache, coord: OverscaledT reconcileReplacement(centroidA, centroidB); } - let centroidXY = new Point(0, 0); + const moreThanOneBorderIntersected = partA.intersectsCount() > 1 || partB.intersectsCount() > 1; if (count > 1) { ib = saveIb; // rewind unprocessed ib so that it is processed again for the next ia. - } else if (neighborDEMTile && neighborDEMTile.dem && !(partA.intersectsCount() > 1 || partB.intersectsCount() > 1)) { + centroidA.centroidXY = centroidB.centroidXY = new Point(0, 0); + } else if (neighborDEMTile && neighborDEMTile.dem && !moreThanOneBorderIntersected) { // If any of a or b crosses more than one tile edge, don't support flat roof. // Now we have 1-1 matching of parts in both tiles that share the edge. Calculate flat base // elevation as average of three points: 2 are edge points (combined span projected to border) and @@ -724,9 +739,14 @@ function updateBorders(context: Context, source: SourceCache, coord: OverscaledT const edge = (i % 2) ? EXTENT - 1 : 0; const height = flatBase(span[0], Math.min(EXTENT - 1, span[1]), edge, neighborDEMTile, nid, i < 2, span[2]); - centroidXY = encodeHeightAsCentroid(height); + centroidA.centroidXY = centroidB.centroidXY = encodeHeightAsCentroid(height); + } else if (moreThanOneBorderIntersected) { + centroidA.centroidXY = centroidB.centroidXY = new Point(0, 0); + } else { + centroidA.centroidXY = bucket.encodeBorderCentroid(partA); + centroidB.centroidXY = nBucket.encodeBorderCentroid(partB); } - centroidA.centroidXY = centroidB.centroidXY = centroidXY; + bucket.writeCentroidToBuffer(centroidA); nBucket.writeCentroidToBuffer(centroidB); } else { diff --git a/src/render/draw_hillshade.js b/src/render/draw_hillshade.js index bee1a94890e..1248d0a8412 100644 --- a/src/render/draw_hillshade.js +++ b/src/render/draw_hillshade.js @@ -23,6 +23,7 @@ export default drawHillshade; function drawHillshade(painter: Painter, sourceCache: SourceCache, layer: HillshadeStyleLayer, tileIDs: Array) { if (painter.renderPass !== 'offscreen' && painter.renderPass !== 'translucent') return; + if (painter.style.disableElevatedTerrain) return; const context = painter.context; @@ -87,11 +88,10 @@ export function prepareDEMTexture(painter: Painter, tile: Tile, dem: DEMData) { const demImage = dem.getPixels(); // Dem encoding should match painters expectations about floating point DEM usage - assert((dem.encoding === 'float') === painter.terrainUseFloatDEM()); if (tile.demTexture) { tile.demTexture.update(demImage, {premultiply: false}); } else { - tile.demTexture = new Texture(context, demImage, painter.terrainUseFloatDEM() ? gl.R32F : gl.RGBA, {premultiply: false}); + tile.demTexture = new Texture(context, demImage, gl.R32F, {premultiply: false}); } tile.needsDEMTextureUpload = false; } @@ -127,7 +127,7 @@ function prepareHillshade(painter: Painter, tile: Tile, layer: HillshadeStyleLay const {tileBoundsBuffer, tileBoundsIndexBuffer, tileBoundsSegments} = painter.getMercatorTileBoundsBuffers(); const definesValues: DynamicDefinesType[] = []; - if (painter.terrainUseFloatDEM()) definesValues.push('TERRAIN_DEM_FLOAT_FORMAT'); + if (painter.linearFloatFilteringSupported()) definesValues.push('TERRAIN_DEM_FLOAT_FORMAT'); painter.getOrCreateProgram('hillshadePrepare', {defines: definesValues}).draw(painter, gl.TRIANGLES, DepthMode.disabled, StencilMode.disabled, ColorMode.unblended, CullFaceMode.disabled, diff --git a/src/render/draw_line.js b/src/render/draw_line.js index 1c82ee74667..f27acc8a1be 100644 --- a/src/render/draw_line.js +++ b/src/render/draw_line.js @@ -39,9 +39,8 @@ export default function drawLine(painter: Painter, sourceCache: SourceCache, lay const capProperty = layer.layout.get('line-cap'); const patternProperty = layer.paint.get('line-pattern'); const image = patternProperty.constantOr((1: any)); - const hasPattern = layer.paint.get('line-pattern').constantOr((1: any)); const hasOpacity = layer.paint.get('line-opacity').constantOr(1.0) !== 1.0; - let useStencilMaskRenderPass = (!hasPattern && hasOpacity); + let useStencilMaskRenderPass = (!image && hasOpacity); const gradient = layer.paint.get('line-gradient'); @@ -102,7 +101,7 @@ export default function drawLine(painter: Painter, sourceCache: SourceCache, lay const matrix = painter.terrain ? coord.projMatrix : null; const uniformValues = image ? - linePatternUniformValues(painter, tile, layer, matrix, pixelRatio) : + linePatternUniformValues(painter, tile, layer, matrix, pixelRatio, [trimStart, trimEnd]) : lineUniformValues(painter, tile, layer, matrix, bucket.lineClipsArray.length, pixelRatio, [trimStart, trimEnd]); if (gradient) { @@ -160,7 +159,7 @@ export default function drawLine(painter: Painter, sourceCache: SourceCache, lay program.draw(painter, gl.TRIANGLES, depthMode, stencilMode, colorMode, CullFaceMode.disabled, uniformValues, layer.id, bucket.layoutVertexBuffer, bucket.indexBuffer, bucket.segments, - layer.paint, painter.transform.zoom, programConfiguration, [bucket.layoutVertexBuffer2]); + layer.paint, painter.transform.zoom, programConfiguration, [bucket.layoutVertexBuffer2, bucket.patternVertexBuffer]); }; if (useStencilMaskRenderPass) { diff --git a/src/render/draw_raster.js b/src/render/draw_raster.js index 9b6f809c7d6..43d9c9a0dd2 100644 --- a/src/render/draw_raster.js +++ b/src/render/draw_raster.js @@ -1,57 +1,89 @@ // @flow +import assert from 'assert'; import ImageSource from '../source/image_source.js'; import StencilMode from '../gl/stencil_mode.js'; import DepthMode from '../gl/depth_mode.js'; import CullFaceMode from '../gl/cull_face_mode.js'; import Texture from './texture.js'; -import {rasterUniformValues} from './program/raster_program.js'; +import {rasterPoleUniformValues, rasterUniformValues} from './program/raster_program.js'; -import type Context from '../gl/context.js'; -import type Painter from './painter.js'; -import type SourceCache from '../source/source_cache.js'; -import type RasterStyleLayer from '../style/style_layer/raster_style_layer.js'; import {OverscaledTileID, CanonicalTileID} from '../source/tile_id.js'; import rasterFade from './raster_fade.js'; import { + calculateGlobeMercatorMatrix, + getGridMatrix, globeNormalizeECEF, globePoleMatrixForTile, globeTileBounds, + globeToMercatorTransition, + getLatitudinalLod, + tileCornersToBounds, } from "../geo/projection/globe_util.js"; +import {GLOBE_ZOOM_THRESHOLD_MIN} from '../geo/projection/globe_constants.js'; import {mat4} from "gl-matrix"; +import {mercatorXfromLng, mercatorYfromLat} from "../geo/mercator_coordinate.js"; +import Transform from '../geo/transform.js'; +import {COLOR_MIX_FACTOR} from '../style/style_layer/raster_style_layer.js'; +import RasterArrayTile from '../source/raster_array_tile.js'; +import RasterArrayTileSource from '../source/raster_array_tile_source.js'; + +import type Tile from '../source/tile.js'; +import type Context from '../gl/context.js'; +import type Painter from './painter.js'; +import type SourceCache from '../source/source_cache.js'; +import type RasterStyleLayer from '../style/style_layer/raster_style_layer.js'; +import type {Source} from '../source/source.js'; +import type {UserManagedTexture} from './texture.js'; +import type {DynamicDefinesType} from '../render/program/program_uniforms.js'; export default drawRaster; const RASTER_COLOR_TEXTURE_UNIT = 2; +type RasterConfig = { + defines: DynamicDefinesType[]; + mix: [number, number, number, number]; + range: [number, number]; + offset: number; + resampling: number; +}; + +function adjustColorMix(colorMix: [number, number, number, number]): [number, number, number, number] { + // Adjust colorMix by the color mix factor to get the proper values for the `computeRasterColorMix` function + // For more details refer to `computeRasterColorMix` in src/style/style_layer/raster_style_layer.js + return [ + colorMix[0] * COLOR_MIX_FACTOR, + colorMix[1] * COLOR_MIX_FACTOR, + colorMix[2] * COLOR_MIX_FACTOR, + 0 + ]; +} + function drawRaster(painter: Painter, sourceCache: SourceCache, layer: RasterStyleLayer, tileIDs: Array, variableOffsets: any, isInitialLoad: boolean) { if (painter.renderPass !== 'translucent') return; if (layer.paint.get('raster-opacity') === 0) return; + const isGlobeProjection = painter.transform.projection.name === 'globe'; + const renderingWithElevation = layer.paint.get('raster-elevation') !== 0.0; + const renderingElevatedOnGlobe = renderingWithElevation && isGlobeProjection; + if (painter.renderElevatedRasterBackface && !renderingElevatedOnGlobe) { + return; + } const context = painter.context; const gl = context.gl; const source = sourceCache.getSource(); - const rasterColor = configureRasterColor(layer, context, gl); - const defines = rasterColor.defines; - let drawAsGlobePole = false; + const rasterConfig = configureRaster(source, layer, context, gl); + if (source instanceof ImageSource && !tileIDs.length) { - if (painter.transform.projection.name !== 'globe') { - return; - } - if (source.onNorthPole) { - drawAsGlobePole = true; - defines.push("PROJECTION_GLOBE_VIEW"); - } else if (source.onSouthPole) { - drawAsGlobePole = true; - defines.push("PROJECTION_GLOBE_VIEW"); - } else { - // Image source without tile ID can only be rendered on the poles + if (!isGlobeProjection) { return; } } - const colorMode = painter.colorModeForDrapableLayerRenderPass(); + const emissiveStrength = layer.paint.get('raster-emissive-strength'); + const colorMode = painter.colorModeForDrapableLayerRenderPass(emissiveStrength); // When rendering to texture, coordinates are already sorted: primary by // proxy id and secondary sort is by Z. @@ -60,57 +92,13 @@ function drawRaster(painter: Painter, sourceCache: SourceCache, layer: RasterSty const align = !painter.options.moving; const textureFilter = layer.paint.get('raster-resampling') === 'nearest' ? gl.NEAREST : gl.LINEAR; - if (drawAsGlobePole) { - const source = sourceCache.getSource(); - if (!(source instanceof ImageSource)) return; - const texture = source.texture; - if (!texture) return; - const sharedBuffers = painter.globeSharedBuffers; - if (!sharedBuffers) return; - - const depthMode = new DepthMode(gl.LEQUAL, DepthMode.ReadWrite, painter.depthRangeFor3D); - const projMatrix = Float32Array.from(painter.transform.projMatrix); - let globeMatrix = globePoleMatrixForTile(0, 0, painter.transform); - const normalizeMatrix = Float32Array.from(globeNormalizeECEF(globeTileBounds(new CanonicalTileID(0, 0, 0)))); - const fade = {opacity: 1, mix: 0}; - - if (painter.terrain) painter.terrain.prepareDrawTile(); - - context.activeTexture.set(gl.TEXTURE0); - texture.bind(textureFilter, gl.CLAMP_TO_EDGE); - context.activeTexture.set(gl.TEXTURE1); - texture.bind(textureFilter, gl.CLAMP_TO_EDGE); - - // Enable trilinear filtering on tiles only beyond 20 degrees pitch, - // to prevent it from compromising image crispness on flat or low tilted maps. - if (texture.useMipmap && context.extTextureFilterAnisotropic && painter.transform.pitch > 20) { - gl.texParameterf(gl.TEXTURE_2D, context.extTextureFilterAnisotropic.TEXTURE_MAX_ANISOTROPY_EXT, context.extTextureFilterAnisotropicMax); - } - - const [ - northPoleBuffer, - southPoleBuffer, - indexBuffer, - segment - ] = sharedBuffers.getPoleBuffers(0, true); - let vertexBuffer; + if (source instanceof ImageSource && !tileIDs.length && (source.onNorthPole || source.onSouthPole)) { + const stencilMode = renderingWithElevation ? painter.stencilModeFor3D() : StencilMode.disabled; if (source.onNorthPole) { - vertexBuffer = northPoleBuffer; - painter.renderDefaultNorthPole = false; + drawPole(true, null, painter, sourceCache, layer, emissiveStrength, rasterConfig, CullFaceMode.disabled, stencilMode); } else { - globeMatrix = mat4.scale(mat4.create(), globeMatrix, [1, -1, 1]); - vertexBuffer = southPoleBuffer; - painter.renderDefaultSouthPole = false; + drawPole(false, null, painter, sourceCache, layer, emissiveStrength, rasterConfig, CullFaceMode.disabled, stencilMode); } - const perspectiveTransform = source.perspectiveTransform; - const uniformValues = rasterUniformValues(projMatrix, normalizeMatrix, globeMatrix, [0, 0], 1, fade, layer, perspectiveTransform || [0, 0], RASTER_COLOR_TEXTURE_UNIT, rasterColor.mix || [0, 0, 0, 0], rasterColor.range || [0, 0]); - const program = painter.getOrCreateProgram('raster', {defines: rasterColor.defines}); - - painter.uploadCommonUniforms(context, program, null); - program.draw( - painter, gl.TRIANGLES, depthMode, StencilMode.disabled, colorMode, CullFaceMode.disabled, - uniformValues, layer.id, vertexBuffer, - indexBuffer, segment); return; } @@ -121,98 +109,394 @@ function drawRaster(painter: Painter, sourceCache: SourceCache, layer: RasterSty painter.stencilConfigForOverlap(tileIDs); const minTileZ = coords[coords.length - 1].overscaledZ; - for (const coord of coords) { - // Set the lower zoom level to sublayer 0, and higher zoom levels to higher sublayers - // Use gl.LESS to prevent double drawing in areas where tiles overlap. - const depthMode = renderingToTexture ? DepthMode.disabled : painter.depthModeForSublayer(coord.overscaledZ - minTileZ, - layer.paint.get('raster-opacity') === 1 ? DepthMode.ReadWrite : DepthMode.ReadOnly, gl.LESS); + if (renderingElevatedOnGlobe) { + rasterConfig.defines.push("PROJECTION_GLOBE_VIEW"); + } + if (renderingWithElevation) { + rasterConfig.defines.push("RENDER_CUTOFF"); + } - const unwrappedTileID = coord.toUnwrapped(); - const tile = sourceCache.getTile(coord); - if (renderingToTexture && !(tile && tile.hasData())) continue; - if (!tile.texture) continue; + const drawTiles = (tiles: Array, cullFaceMode: CullFaceMode, elevatedStencilMode?: StencilMode) => { + for (const coord of tiles) { + const unwrappedTileID = coord.toUnwrapped(); + const tile = sourceCache.getTile(coord); + if (renderingToTexture && !(tile && tile.hasData())) continue; + + context.activeTexture.set(gl.TEXTURE0); + const textureDescriptor = getTextureDescriptor(tile, source, layer, rasterConfig); + if (!textureDescriptor || !textureDescriptor.texture) continue; + const {texture, mix: rasterColorMix, offset: rasterColorOffset, tileSize, buffer} = textureDescriptor; + + let depthMode; + let projMatrix; + if (renderingToTexture) { + depthMode = DepthMode.disabled; + projMatrix = coord.projMatrix; + } else if (renderingWithElevation) { + depthMode = new DepthMode(gl.LEQUAL, DepthMode.ReadWrite, painter.depthRangeFor3D); + projMatrix = isGlobeProjection ? Float32Array.from(painter.transform.expandedFarZProjMatrix) : painter.transform.calculateProjMatrix(unwrappedTileID, align); + } else { + // Set the lower zoom level to sublayer 0, and higher zoom levels to higher sublayers + // Use gl.LESS to prevent double drawing in areas where tiles overlap. + depthMode = painter.depthModeForSublayer(coord.overscaledZ - minTileZ, + layer.paint.get('raster-opacity') === 1 ? DepthMode.ReadWrite : DepthMode.ReadOnly, gl.LESS); + projMatrix = painter.transform.calculateProjMatrix(unwrappedTileID, align); + } - const projMatrix = (renderingToTexture) ? coord.projMatrix : - painter.transform.calculateProjMatrix(unwrappedTileID, align); + const stencilMode = painter.terrain && renderingToTexture ? + painter.terrain.stencilModeForRTTOverlap(coord) : + stencilModes[coord.overscaledZ]; - const stencilMode = painter.terrain && renderingToTexture ? - painter.terrain.stencilModeForRTTOverlap(coord) : - stencilModes[coord.overscaledZ]; + const rasterFadeDuration = isInitialLoad ? 0 : layer.paint.get('raster-fade-duration'); + tile.registerFadeDuration(rasterFadeDuration); - const rasterFadeDuration = isInitialLoad ? 0 : layer.paint.get('raster-fade-duration'); - tile.registerFadeDuration(rasterFadeDuration); + const parentTile = sourceCache.findLoadedParent(coord, 0); + const fade = rasterFade(tile, parentTile, sourceCache, painter.transform, rasterFadeDuration); + if (painter.terrain) painter.terrain.prepareDrawTile(); - const parentTile = sourceCache.findLoadedParent(coord, 0); - const fade = rasterFade(tile, parentTile, sourceCache, painter.transform, rasterFadeDuration); - if (painter.terrain) painter.terrain.prepareDrawTile(); + let parentScaleBy, parentTL; - let parentScaleBy, parentTL; + context.activeTexture.set(gl.TEXTURE0); + texture.bind(textureFilter, gl.CLAMP_TO_EDGE); - context.activeTexture.set(gl.TEXTURE0); - if (tile.texture) { - tile.texture.bind(textureFilter, gl.CLAMP_TO_EDGE); - } + context.activeTexture.set(gl.TEXTURE1); - context.activeTexture.set(gl.TEXTURE1); + if (parentTile) { + if (parentTile.texture) { + parentTile.texture.bind(textureFilter, gl.CLAMP_TO_EDGE); + } + parentScaleBy = Math.pow(2, parentTile.tileID.overscaledZ - tile.tileID.overscaledZ); + parentTL = [tile.tileID.canonical.x * parentScaleBy % 1, tile.tileID.canonical.y * parentScaleBy % 1]; - if (parentTile) { - if (parentTile.texture) { - parentTile.texture.bind(textureFilter, gl.CLAMP_TO_EDGE); + } else { + texture.bind(textureFilter, gl.CLAMP_TO_EDGE); } - parentScaleBy = Math.pow(2, parentTile.tileID.overscaledZ - tile.tileID.overscaledZ); - parentTL = [tile.tileID.canonical.x * parentScaleBy % 1, tile.tileID.canonical.y * parentScaleBy % 1]; - } else if (tile.texture) { - tile.texture.bind(textureFilter, gl.CLAMP_TO_EDGE); + // Enable trilinear filtering on tiles only beyond 20 degrees pitch, + // to prevent it from compromising image crispness on flat or low tilted maps. + if (texture.useMipmap && context.extTextureFilterAnisotropic && painter.transform.pitch > 20) { + gl.texParameterf(gl.TEXTURE_2D, context.extTextureFilterAnisotropic.TEXTURE_MAX_ANISOTROPY_EXT, context.extTextureFilterAnisotropicMax); + } + + const tr = painter.transform; + let perspectiveTransform: [number, number]; + const cutoffParams = renderingWithElevation ? cutoffParamsForElevation(tr) : [0, 0, 0, 0]; + + let normalizeMatrix: Float32Array; + let globeMatrix: Float32Array; + let globeMercatorMatrix: Float32Array; + let mercatorCenter: [number, number]; + let gridMatrix: Float32Array; + let latitudinalLod = 0; + + if (renderingElevatedOnGlobe && source instanceof ImageSource && source.coordinates.length > 3) { + normalizeMatrix = Float32Array.from(globeNormalizeECEF(globeTileBounds(new CanonicalTileID(0, 0, 0)))); + globeMatrix = Float32Array.from(tr.globeMatrix); + globeMercatorMatrix = Float32Array.from(calculateGlobeMercatorMatrix(tr)); + mercatorCenter = [mercatorXfromLng(tr.center.lng), mercatorYfromLat(tr.center.lat)]; + perspectiveTransform = source.elevatedGlobePerspectiveTransform; + gridMatrix = source.elevatedGlobeGridMatrix || new Float32Array(9); + } else if (renderingElevatedOnGlobe) { + const tileBounds = tileCornersToBounds(coord.canonical); + latitudinalLod = getLatitudinalLod(tileBounds.getCenter().lat); + normalizeMatrix = Float32Array.from(globeNormalizeECEF(globeTileBounds(coord.canonical))); + globeMatrix = Float32Array.from(tr.globeMatrix); + globeMercatorMatrix = Float32Array.from(calculateGlobeMercatorMatrix(tr)); + mercatorCenter = [mercatorXfromLng(tr.center.lng), mercatorYfromLat(tr.center.lat)]; + perspectiveTransform = [0, 0]; + gridMatrix = Float32Array.from(getGridMatrix(coord.canonical, tileBounds, latitudinalLod, tr.worldSize / tr._pixelsPerMercatorPixel)); + } else { + perspectiveTransform = source instanceof ImageSource ? source.perspectiveTransform : [0, 0]; + normalizeMatrix = new Float32Array(16); + globeMatrix = new Float32Array(9); + globeMercatorMatrix = new Float32Array(16); + mercatorCenter = [0, 0]; + gridMatrix = new Float32Array(9); + } + + const uniformValues = rasterUniformValues( + projMatrix, + normalizeMatrix, + globeMatrix, + globeMercatorMatrix, + gridMatrix, + parentTL || [0, 0], + globeToMercatorTransition(painter.transform.zoom), + mercatorCenter, + cutoffParams, + parentScaleBy || 1, + fade, + layer, + perspectiveTransform, + renderingWithElevation ? layer.paint.get('raster-elevation') : 0.0, + RASTER_COLOR_TEXTURE_UNIT, + rasterColorMix, + rasterColorOffset, + rasterConfig.range, + tileSize, + buffer, + emissiveStrength + ); + const affectedByFog = painter.isTileAffectedByFog(coord); + + const program = painter.getOrCreateProgram('raster', {defines: rasterConfig.defines, overrideFog: affectedByFog}); + + painter.uploadCommonUniforms(context, program, unwrappedTileID); + + if (source instanceof ImageSource) { + const elevatedGlobeVertexBuffer = source.elevatedGlobeVertexBuffer; + const elevatedGlobeIndexBuffer = source.elevatedGlobeIndexBuffer; + if (renderingToTexture || !isGlobeProjection) { + if (source.boundsBuffer && source.boundsSegments) program.draw( + painter, gl.TRIANGLES, depthMode, StencilMode.disabled, colorMode, CullFaceMode.disabled, + uniformValues, layer.id, source.boundsBuffer, + painter.quadTriangleIndexBuffer, source.boundsSegments); + } else if (elevatedGlobeVertexBuffer && elevatedGlobeIndexBuffer) { + const segments = tr.zoom <= GLOBE_ZOOM_THRESHOLD_MIN ? + source.elevatedGlobeSegments : + source.getSegmentsForLongitude(tr.center.lng); + if (segments) { + program.draw( + painter, gl.TRIANGLES, depthMode, StencilMode.disabled, colorMode, cullFaceMode, + uniformValues, layer.id, elevatedGlobeVertexBuffer, + elevatedGlobeIndexBuffer, segments); + } + } + } else if (renderingElevatedOnGlobe) { + depthMode = new DepthMode(gl.LEQUAL, DepthMode.ReadOnly, painter.depthRangeFor3D); + const sharedBuffers = painter.globeSharedBuffers; + if (sharedBuffers) { + const [buffer, indexBuffer, segments] = sharedBuffers.getGridBuffers(latitudinalLod, false); + assert(buffer); + assert(indexBuffer); + assert(segments); + program.draw(painter, gl.TRIANGLES, depthMode, elevatedStencilMode || stencilMode, painter.colorModeForRenderPass(), cullFaceMode, uniformValues, layer.id, buffer, indexBuffer, segments); + } + } else { + const {tileBoundsBuffer, tileBoundsIndexBuffer, tileBoundsSegments} = painter.getTileBoundsBuffers(tile); + + program.draw(painter, gl.TRIANGLES, depthMode, stencilMode, colorMode, CullFaceMode.disabled, + uniformValues, layer.id, tileBoundsBuffer, + tileBoundsIndexBuffer, tileBoundsSegments); + } } - // Enable trilinear filtering on tiles only beyond 20 degrees pitch, - // to prevent it from compromising image crispness on flat or low tilted maps. - if (tile.texture && tile.texture.useMipmap && context.extTextureFilterAnisotropic && painter.transform.pitch > 20) { - gl.texParameterf(gl.TEXTURE_2D, context.extTextureFilterAnisotropic.TEXTURE_MAX_ANISOTROPY_EXT, context.extTextureFilterAnisotropicMax); + if (!(source instanceof ImageSource) && renderingElevatedOnGlobe) { + for (const coord of tiles) { + const topCap = coord.canonical.y === 0; + const bottomCap = coord.canonical.y === (1 << coord.canonical.z) - 1; + if (topCap) { + drawPole(true, coord, painter, sourceCache, layer, emissiveStrength, rasterConfig, cullFaceMode, elevatedStencilMode || StencilMode.disabled); + } + if (bottomCap) { + drawPole(false, coord, painter, sourceCache, layer, emissiveStrength, rasterConfig, cullFaceMode === CullFaceMode.frontCW ? CullFaceMode.backCW : CullFaceMode.frontCW, elevatedStencilMode || StencilMode.disabled); + } + } } + }; - const perspectiveTransform = source instanceof ImageSource ? source.perspectiveTransform : [0, 0]; - const emptyMatrix = new Float32Array(16); - const uniformValues = rasterUniformValues(projMatrix, emptyMatrix, emptyMatrix, parentTL || [0, 0], parentScaleBy || 1, fade, layer, perspectiveTransform, RASTER_COLOR_TEXTURE_UNIT, rasterColor.mix || [0, 0, 0, 0], rasterColor.range || [0, 0]); - const affectedByFog = painter.isTileAffectedByFog(coord); + if (renderingElevatedOnGlobe) { + if (painter.renderElevatedRasterBackface) { + drawTiles(coords, CullFaceMode.backCW, painter.stencilModeFor3D()); + } else { + drawTiles(coords, CullFaceMode.frontCW, painter.stencilModeFor3D()); + } + } else { + drawTiles(coords, CullFaceMode.disabled, undefined); + } - const program = painter.getOrCreateProgram('raster', {defines: rasterColor.defines, overrideFog: affectedByFog}); + painter.resetStencilClippingMasks(); +} - painter.uploadCommonUniforms(context, program, unwrappedTileID); +function drawPole(isNorth: boolean, coord: ?OverscaledTileID, painter: Painter, sourceCache: SourceCache, layer: RasterStyleLayer, emissiveStrength: number, rasterConfig: any, cullFaceMode: CullFaceMode, stencilMode: StencilMode) { + const source = sourceCache.getSource(); + const sharedBuffers = painter.globeSharedBuffers; + if (!sharedBuffers) return; - if (source instanceof ImageSource) { - if (source.boundsBuffer && source.boundsSegments) program.draw( - painter, gl.TRIANGLES, depthMode, StencilMode.disabled, colorMode, CullFaceMode.disabled, - uniformValues, layer.id, source.boundsBuffer, - painter.quadTriangleIndexBuffer, source.boundsSegments); - } else { - const {tileBoundsBuffer, tileBoundsIndexBuffer, tileBoundsSegments} = painter.getTileBoundsBuffers(tile); + let tile; + if (coord) { + tile = sourceCache.getTile(coord); + } + let texture; + let globeMatrix; + if (source instanceof ImageSource) { + texture = source.texture; + globeMatrix = globePoleMatrixForTile(0, 0, painter.transform); + } else if (tile && coord) { + texture = tile.texture; + globeMatrix = globePoleMatrixForTile(coord.canonical.z, coord.canonical.x, painter.transform); + } + if (!texture || !globeMatrix) return; + + if (!isNorth) { + globeMatrix = mat4.scale(mat4.create(), globeMatrix, [1, -1, 1]); + } + + const context = painter.context; + const gl = context.gl; + const textureFilter = layer.paint.get('raster-resampling') === 'nearest' ? gl.NEAREST : gl.LINEAR; + const colorMode = painter.colorModeForDrapableLayerRenderPass(emissiveStrength); + const defines = rasterConfig.defines; + defines.push("GLOBE_POLES"); + + const depthMode = new DepthMode(gl.LEQUAL, DepthMode.ReadWrite, painter.depthRangeFor3D); + const projMatrix = Float32Array.from(painter.transform.expandedFarZProjMatrix); + const normalizeMatrix = Float32Array.from(globeNormalizeECEF(globeTileBounds(new CanonicalTileID(0, 0, 0)))); + const fade = {opacity: 1, mix: 0}; + + if (painter.terrain) painter.terrain.prepareDrawTile(); + + context.activeTexture.set(gl.TEXTURE0); + texture.bind(textureFilter, gl.CLAMP_TO_EDGE); + context.activeTexture.set(gl.TEXTURE1); + texture.bind(textureFilter, gl.CLAMP_TO_EDGE); + + // Enable trilinear filtering on tiles only beyond 20 degrees pitch, + // to prevent it from compromising image crispness on flat or low tilted maps. + if (texture.useMipmap && context.extTextureFilterAnisotropic && painter.transform.pitch > 20) { + gl.texParameterf(gl.TEXTURE_2D, context.extTextureFilterAnisotropic.TEXTURE_MAX_ANISOTROPY_EXT, context.extTextureFilterAnisotropicMax); + } - program.draw(painter, gl.TRIANGLES, depthMode, stencilMode, colorMode, CullFaceMode.disabled, - uniformValues, layer.id, tileBoundsBuffer, - tileBoundsIndexBuffer, tileBoundsSegments); + const [ + northPoleBuffer, + southPoleBuffer, + indexBuffer, + segment + ] = coord ? sharedBuffers.getPoleBuffers(coord.canonical.z, false) : sharedBuffers.getPoleBuffers(0, true); + const elevation = layer.paint.get('raster-elevation'); + let vertexBuffer; + if (isNorth) { + vertexBuffer = northPoleBuffer; + painter.renderDefaultNorthPole = elevation !== 0.0; + } else { + vertexBuffer = southPoleBuffer; + painter.renderDefaultSouthPole = elevation !== 0.0; + } + const rasterColorMix = adjustColorMix(rasterConfig.mix); + const uniformValues = rasterPoleUniformValues(projMatrix, normalizeMatrix, globeMatrix, globeToMercatorTransition(painter.transform.zoom), fade, layer, [0, 0], elevation, RASTER_COLOR_TEXTURE_UNIT, rasterColorMix, rasterConfig.offset, rasterConfig.range, emissiveStrength); + const program = painter.getOrCreateProgram('raster', {defines}); + + painter.uploadCommonUniforms(context, program, null); + program.draw( + painter, gl.TRIANGLES, depthMode, stencilMode, colorMode, cullFaceMode, + uniformValues, layer.id, vertexBuffer, + indexBuffer, segment); +} + +// Configure a fade out effect for elevated raster layers when they're close to the camera +function cutoffParamsForElevation(tr: Transform): [number, number, number, number] { + const near = tr._nearZ; + const far = tr.projection.farthestPixelDistance(tr); + const zRange = far - near; + const fadeRangePixels = tr.height * 0.2; + const cutoffDistance = near + fadeRangePixels; + const relativeCutoffDistance = ((cutoffDistance - near) / zRange); + const relativeCutoffFadeDistance = ((cutoffDistance - fadeRangePixels - near) / zRange); + return [near, far, relativeCutoffFadeDistance, relativeCutoffDistance]; +} + +export function prepare(layer: RasterStyleLayer, sourceCache: SourceCache, _: Painter): void { + const source = sourceCache.getSource(); + if (!(source instanceof RasterArrayTileSource) || !source.loaded()) return; + + const sourceLayer = layer.sourceLayer || (source.rasterLayerIds && source.rasterLayerIds[0]); + if (!sourceLayer) return; + + const band = layer.paint.get('raster-array-band') || source.getInitialBand(sourceLayer); + if (band == null) return; + + // $FlowFixMe[incompatible-type] + const tiles: Array = sourceCache.getIds().map(id => sourceCache.getTileByID(id)); + for (const tile of tiles) { + if (tile.updateNeeded(sourceLayer, band)) { + source.prepareTile(tile, sourceLayer, band); } } +} - painter.resetStencilClippingMasks(); +export type TextureDescriptor = { + texture: ?Texture | ?UserManagedTexture, + mix: [number, number, number, number], + offset: number, + buffer: number, + tileSize: number, +}; + +function getTextureDescriptor(tile: ?Tile | ?RasterArrayTile, source: Source | RasterArrayTileSource, layer: RasterStyleLayer, rasterConfig: RasterConfig): TextureDescriptor | void { + if (!tile) return; + + if (source instanceof RasterArrayTileSource && tile instanceof RasterArrayTile) { + return ((source.getTextureDescriptor(tile, layer, true): any): TextureDescriptor); + } + + return { + texture: tile.texture, + mix: adjustColorMix(rasterConfig.mix), + offset: rasterConfig.offset, + buffer: 0, + tileSize: 1, + }; } -function configureRasterColor (layer: RasterStyleLayer, context: Context, gl: WebGL2RenderingContext) { - const defines = []; - let mix; - let range; +function configureRaster(source: Source, layer: RasterStyleLayer, context: Context, gl: WebGL2RenderingContext): RasterConfig { + const isRasterColor = layer.paint.get('raster-color'); + const isRasterArray = source.type === 'raster-array'; - if (layer.paint.get('raster-color')) { - defines.push('RASTER_COLOR'); - mix = layer.paint.get('raster-color-mix'); - range = layer.paint.get('raster-color-range'); + const defines: DynamicDefinesType[] = []; + const inputResampling = layer.paint.get('raster-resampling'); + const inputMix = layer.paint.get('raster-color-mix'); + let range = layer.paint.get('raster-color-range'); + + // Unpack the offset for use in a separate uniform + const mix = [inputMix[0], inputMix[1], inputMix[2], 0]; + const offset = inputMix[3]; + + let resampling = inputResampling === 'nearest' ? gl.NEAREST : gl.LINEAR; + + if (isRasterArray) { + defines.push('RASTER_ARRAY'); + + // Raster-array sources require RASTER_COLOR for raster array data decoding and binary 0/1 mask interpolation + if (!isRasterColor) defines.push('RASTER_COLOR'); + + // Raster-array sources require in-shader linear interpolation in order to decode without + // artifacts, so force nearest filtering. + if (inputResampling === 'linear') defines.push('RASTER_ARRAY_LINEAR'); + resampling = gl.NEAREST; + if (!range) { + if (source.rasterLayers) { + const foundLayer = source.rasterLayers.find(({id}) => id === layer.sourceLayer); + if (foundLayer && foundLayer.fields && foundLayer.fields.range) { + range = foundLayer.fields.range; + } + } + } + } + + // In all cases, having checked for a preferred color ramp, we now supply a default, + // to be used in case nothing else has been specified. + range = range || [0, 1]; + + if (isRasterColor) { + defines.push('RASTER_COLOR'); // Allocate a texture if not allocated context.activeTexture.set(gl.TEXTURE2); + + // Update the color ramp defensively. Duplicate calls with the same bounds + // will not trigger recomputation. + layer.updateColorRamp(range); + let tex = layer.colorRampTexture; if (!tex) tex = layer.colorRampTexture = new Texture(context, layer.colorRamp, gl.RGBA); tex.bind(gl.LINEAR, gl.CLAMP_TO_EDGE); } - return {mix, range, defines}; + + return { + mix, + range, + offset, + defines, + resampling + }; } diff --git a/src/render/draw_raster_particle.js b/src/render/draw_raster_particle.js new file mode 100644 index 00000000000..038e4255ce6 --- /dev/null +++ b/src/render/draw_raster_particle.js @@ -0,0 +1,510 @@ +// @flow + +import browser from '../util/browser.js'; +import Color from '../style-spec/util/color.js'; +import ColorMode from '../gl/color_mode.js'; +import CullFaceMode from '../gl/cull_face_mode.js'; +import DepthMode from '../gl/depth_mode.js'; +import StencilMode from '../gl/stencil_mode.js'; +import {rasterParticleUniformValues, rasterParticleTextureUniformValues, rasterParticleDrawUniformValues, rasterParticleUpdateUniformValues} from './program/raster_particle_program.js'; +import {computeRasterColorMix, computeRasterColorOffset} from './raster.js'; +import {COLOR_RAMP_RES} from '../style/style_layer/raster_particle_style_layer.js'; +import RasterArrayTile from '../source/raster_array_tile.js'; +import RasterArrayTileSource from '../source/raster_array_tile_source.js'; + +import type {DynamicDefinesType} from "./program/program_uniforms"; +import type Painter from './painter.js'; +import type SourceCache from '../source/source_cache.js'; +import type RasterParticleStyleLayer from '../style/style_layer/raster_particle_style_layer.js'; +import {OverscaledTileID, neighborCoord} from '../source/tile_id.js'; +import { + calculateGlobeMercatorMatrix, + getGridMatrix, + globeNormalizeECEF, + globeTileBounds, + globeToMercatorTransition, + getLatitudinalLod, + tileCornersToBounds} from "../geo/projection/globe_util.js"; +import RasterParticleState from './raster_particle_state.js'; +import Texture from './texture.js'; +import {mercatorXfromLng, mercatorYfromLat} from "../geo/mercator_coordinate.js"; +import Transform from '../geo/transform.js'; +import rasterFade from './raster_fade.js'; +import assert from 'assert'; + +export default drawRasterParticle; + +const VELOCITY_TEXTURE_UNIT = 0; +const RASTER_PARTICLE_TEXTURE_UNIT = 1; +const RASTER_COLOR_TEXTURE_UNIT = 2; +const SPEED_MAX_VALUE = 0.3; + +function drawRasterParticle(painter: Painter, sourceCache: SourceCache, layer: RasterParticleStyleLayer, tileIDs: Array, _: any, isInitialLoad: boolean) { + if (painter.renderPass === 'offscreen') { + renderParticlesToTexture(painter, sourceCache, layer, tileIDs); + } + + if (painter.renderPass === 'translucent') { + renderTextureToMap(painter, sourceCache, layer, tileIDs, isInitialLoad); + painter.style.map.triggerRepaint(); + } +} + +function renderParticlesToTexture(painter: Painter, sourceCache: SourceCache, layer: RasterParticleStyleLayer, tileIDs: Array) { + if (!tileIDs.length) { + return; + } + + const context = painter.context; + const gl = context.gl; + const source = sourceCache.getSource(); + if (!(source instanceof RasterArrayTileSource)) return; + + // update layer resources + + const particleTextureDimension = Math.ceil(Math.sqrt(layer.paint.get('raster-particle-count'))); + let particleFramebuffer = layer.particleFramebuffer; + if (!particleFramebuffer) { + particleFramebuffer = layer.particleFramebuffer = context.createFramebuffer(particleTextureDimension, particleTextureDimension, true, null); + } else if (particleFramebuffer.width !== particleTextureDimension) { + assert(particleFramebuffer.width === particleFramebuffer.height); + particleFramebuffer.destroy(); + particleFramebuffer = layer.particleFramebuffer = context.createFramebuffer(particleTextureDimension, particleTextureDimension, true, null); + } + + // acquire and update tiles + + const tiles: Array<[OverscaledTileID, TileData, RasterParticleState, boolean]> = []; + for (const id of tileIDs) { + const tile = sourceCache.getTile(id); + if (!(tile instanceof RasterArrayTile)) continue; + + const data = getTileData(tile, source, layer); + if (!data) continue; + assert(data.texture); + + const textureSize = [tile.tileSize, tile.tileSize]; + let tileFramebuffer = layer.tileFramebuffer; + if (!tileFramebuffer) { + const fbWidth = textureSize[0]; + const fbHeight = textureSize[1]; + tileFramebuffer = layer.tileFramebuffer = context.createFramebuffer(fbWidth, fbHeight, true, null); + } + assert(tileFramebuffer.width === textureSize[0] && tileFramebuffer.height === textureSize[1]); + + let state = tile.rasterParticleState; + if (!state) { + state = tile.rasterParticleState = new RasterParticleState(context, id, textureSize, particleTextureDimension); + } + + const renderBackground = state.update(layer.lastInvalidatedAt); + + if (state.particleTextureDimension !== particleTextureDimension) { + state.setParticleTextureDimension(id, particleTextureDimension); + } + + const t = state.targetColorTexture; + state.targetColorTexture = state.backgroundColorTexture; + state.backgroundColorTexture = t; + + const p = state.particleTexture0; + state.particleTexture0 = state.particleTexture1; + state.particleTexture1 = p; + + tiles.push([id, data, state, renderBackground]); + } + + if (tiles.length === 0) { + return; + } + + const now = browser.now(); + const frameDeltaSeconds = layer.previousDrawTimestamp ? 0.001 * (now - layer.previousDrawTimestamp) : 0.0167; + layer.previousDrawTimestamp = now; + + if (layer.hasColorMap()) { + // Allocate a texture if not allocated + context.activeTexture.set(gl.TEXTURE0 + RASTER_COLOR_TEXTURE_UNIT); + let tex = layer.colorRampTexture; + if (!tex) tex = layer.colorRampTexture = new Texture(context, layer.colorRamp, gl.RGBA); + tex.bind(gl.LINEAR, gl.CLAMP_TO_EDGE); + } + + // render and update the particle state + + context.bindFramebuffer.set(layer.tileFramebuffer.framebuffer); + renderBackground(painter, layer, tiles); + renderParticles(painter, sourceCache, layer, tiles); + context.bindFramebuffer.set(layer.particleFramebuffer.framebuffer); + updateParticles(painter, layer, tiles, frameDeltaSeconds); +} + +type TileData = {| + texture: Texture, + textureOffset: [number, number], + tileSize: number, + scalarData: boolean, + scale: [number, number, number, number], + offset: number, + defines: DynamicDefinesType[] +|}; + +function getTileData(tile: RasterArrayTile, source: RasterArrayTileSource, layer: RasterParticleStyleLayer): ?TileData { + if (!tile) { + return null; + } + + const textureDesc = source.getTextureDescriptor(tile, layer, true); + + if (!textureDesc) { + return null; + } + let {texture, mix, offset, tileSize, buffer, format} = textureDesc; + if (!texture || !format) { + return null; + } + + let scalarData = false; + if (format === 'uint32') { + scalarData = true; + mix[3] = 0; + mix = computeRasterColorMix(COLOR_RAMP_RES, mix, [0, layer.paint.get('raster-particle-max-speed')]); + offset = computeRasterColorOffset(COLOR_RAMP_RES, offset, [0, layer.paint.get('raster-particle-max-speed')]); + } + const dataFormatDefine = { + uint8: 'DATA_FORMAT_UINT8', + uint16: 'DATA_FORMAT_UINT16', + uint32: 'DATA_FORMAT_UINT32', + }[format]; + + return { + texture, + textureOffset: [ buffer / (tileSize + 2 * buffer), tileSize / (tileSize + 2 * buffer)], + tileSize, + scalarData, + scale: mix, + offset, + defines: ['RASTER_ARRAY', dataFormatDefine] + }; +} + +function renderBackground(painter: Painter, layer: RasterParticleStyleLayer, tiles: Array<[OverscaledTileID, TileData, RasterParticleState, boolean]>) { + const context = painter.context; + const gl = context.gl; + const framebuffer = layer.tileFramebuffer; + + context.activeTexture.set(gl.TEXTURE0); + + const textureUnit = 0; + const opacityValue = fadeOpacityCurve(layer.paint.get('raster-particle-fade-opacity-factor')); + const uniforms = rasterParticleTextureUniformValues(textureUnit, opacityValue); + const program = painter.getOrCreateProgram('rasterParticleTexture', {defines: [], overrideFog: false}); + + for (const tile of tiles) { + const [, , particleState, renderBackground] = tile; + framebuffer.colorAttachment.set(particleState.targetColorTexture.texture); + context.viewport.set([0, 0, framebuffer.width, framebuffer.height]); + context.clear({color: Color.transparent}); + if (!renderBackground) continue; + particleState.backgroundColorTexture.bind(gl.NEAREST, gl.CLAMP_TO_EDGE); + program.draw( + painter, + gl.TRIANGLES, + DepthMode.disabled, + StencilMode.disabled, + ColorMode.alphaBlended, + CullFaceMode.disabled, + uniforms, + layer.id, + painter.viewportBuffer, + painter.quadTriangleIndexBuffer, + painter.viewportSegments); + } +} + +function fadeOpacityCurve(fadeOpacityFactor: number): number { + // The opacity factor has a very non-linear visual effect in its linear [0, 1] range. Particle trails + // are visible only roughly after a value of 0.7. Applying a curve which significantly boosts values + // close to 0 ensures that linearly increasing the fade opacity factor from 0 to 1 results in a visually + // linear increase in trail length. + // + // A curve of the form (1 + a)(x / (x + a)) boosts values close to 0, and results in a number in the + // [0, 1] range. + + const x = fadeOpacityFactor; + const a = 0.05; + return (1.0 + a) * x / (x + a); +} + +function resetRateCurve(resetRate: number): number { + // Even small reset rates (close to zero) result in fast reset period. Values at >0.4 visually appear to + // respawn very fast. Applying a power curve (x^n) to the reset rate ensures that we can linearly increase + // the reset rate from 0 to 1 and see a more gradual increase in the reset rate. + + return Math.pow(resetRate, 6.0); +} + +function renderParticles(painter: Painter, sourceCache: SourceCache, layer: RasterParticleStyleLayer, tiles: Array<[OverscaledTileID, TileData, RasterParticleState, boolean]>) { + const context = painter.context; + const gl = context.gl; + + const framebuffer = layer.tileFramebuffer; + const isGlobeProjection = painter.transform.projection.name === 'globe'; + const maxSpeed = layer.paint.get('raster-particle-max-speed'); + for (const targetTile of tiles) { + const [targetTileID, targetTileData, targetTileState, ] = targetTile; + + context.activeTexture.set(gl.TEXTURE0 + VELOCITY_TEXTURE_UNIT); + targetTileData.texture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE); + framebuffer.colorAttachment.set(targetTileState.targetColorTexture.texture); + const defines = targetTileData.defines; + const program = painter.getOrCreateProgram('rasterParticleDraw', {defines, overrideFog: false}); + + context.activeTexture.set(gl.TEXTURE0 + RASTER_PARTICLE_TEXTURE_UNIT); + const tileIDs = targetTileData.scalarData ? [] : [0, 1, 2, 3].map(idx => neighborCoord[idx](targetTileID)); + tileIDs.push(targetTileID); + const x = targetTileID.canonical.x; + const y = targetTileID.canonical.y; + for (const tileID of tileIDs) { + const tile = sourceCache.getTile(isGlobeProjection ? tileID.wrapped() : tileID); + if (!tile) continue; + const state = tile.rasterParticleState; + if (!state) continue; + + // NOTE: tiles adjacent to the antimeridian need their x coordinates shifted by (2^z) in order for (nx - x) + // to be contained in [-1, 1]. + const wrapDelta = tileID.wrap - targetTileID.wrap; + const nx = tileID.canonical.x + (1 << tileID.canonical.z) * wrapDelta; + const ny = tileID.canonical.y; + + state.particleTexture0.bind(gl.NEAREST, gl.CLAMP_TO_EDGE); + const rasterParticleTextureRes = state.particleTexture0.size; + assert(rasterParticleTextureRes[0] === rasterParticleTextureRes[1]); + const rasterParticleTextureSideLen = rasterParticleTextureRes[0]; + const tileOffset = [nx - x, ny - y]; + const uniforms = rasterParticleDrawUniformValues( + RASTER_PARTICLE_TEXTURE_UNIT, + rasterParticleTextureSideLen, + tileOffset, + VELOCITY_TEXTURE_UNIT, + targetTileData.texture.size, + RASTER_COLOR_TEXTURE_UNIT, + maxSpeed, + targetTileData.textureOffset, + targetTileData.scale, + targetTileData.offset + ); + program.draw( + painter, + gl.POINTS, + DepthMode.disabled, + StencilMode.disabled, + ColorMode.alphaBlended, + CullFaceMode.disabled, + uniforms, + layer.id, + state.particleIndexBuffer, + undefined, + state.particleSegment + ); + } + } +} + +function updateParticles(painter: Painter, layer: RasterParticleStyleLayer, tiles: Array<[OverscaledTileID, TileData, RasterParticleState, boolean]>, frameDeltaSeconds: number) { + const context = painter.context; + const gl = context.gl; + + const maxSpeed = layer.paint.get('raster-particle-max-speed'); + const speedFactor = frameDeltaSeconds * layer.paint.get('raster-particle-speed-factor') * SPEED_MAX_VALUE; + const resetRateFactor = layer.paint.get('raster-particle-reset-rate-factor'); + const resetRate = resetRateCurve(0.01 + resetRateFactor * 1.0); + const particleFramebuffer = layer.particleFramebuffer; + context.viewport.set([0, 0, particleFramebuffer.width, particleFramebuffer.height]); + + for (const tile of tiles) { + const [, data, state, ] = tile; + + context.activeTexture.set(gl.TEXTURE0 + VELOCITY_TEXTURE_UNIT); + data.texture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE); + context.activeTexture.set(gl.TEXTURE0 + RASTER_PARTICLE_TEXTURE_UNIT); + const particleTexture = state.particleTexture0; + particleTexture.bind(gl.NEAREST, gl.CLAMP_TO_EDGE); + const uniforms = rasterParticleUpdateUniformValues( + RASTER_PARTICLE_TEXTURE_UNIT, + particleTexture.size[0], + VELOCITY_TEXTURE_UNIT, + data.texture.size, + maxSpeed, + speedFactor, + resetRate, + data.textureOffset, + data.scale, + data.offset + ); + particleFramebuffer.colorAttachment.set(state.particleTexture1.texture); + context.clear({color: Color.transparent}); + const updateProgram = painter.getOrCreateProgram('rasterParticleUpdate', {defines: data.defines}); + updateProgram.draw( + painter, + gl.TRIANGLES, + DepthMode.disabled, + StencilMode.disabled, + ColorMode.unblended, + CullFaceMode.disabled, + uniforms, + layer.id, + painter.viewportBuffer, + painter.quadTriangleIndexBuffer, + painter.viewportSegments); + } +} + +function renderTextureToMap(painter: Painter, sourceCache: SourceCache, layer: RasterParticleStyleLayer, tileIDs: Array, _: boolean) { + const context = painter.context; + const gl = context.gl; + + const rasterElevation = 250.0; + const align = !painter.options.moving; + const isGlobeProjection = painter.transform.projection.name === 'globe'; + + if (!tileIDs.length) { + return; + } + const [stencilModes, coords] = painter.stencilConfigForOverlap(tileIDs); + + const defines: DynamicDefinesType[] = []; + if (isGlobeProjection) { + defines.push("PROJECTION_GLOBE_VIEW"); + } + + const stencilMode = painter.stencilModeFor3D(); + + for (const coord of coords) { + const unwrappedTileID = coord.toUnwrapped(); + const tile = sourceCache.getTile(coord); + if (!tile.rasterParticleState) continue; + const particleState = tile.rasterParticleState; + + // NOTE: workaround for https://mapbox.atlassian.net/browse/GLJS-675 + const rasterFadeDuration = 100; + tile.registerFadeDuration(rasterFadeDuration); + + const parentTile = sourceCache.findLoadedParent(coord, 0); + const fade = rasterFade(tile, parentTile, sourceCache, painter.transform, rasterFadeDuration); + if (painter.terrain) painter.terrain.prepareDrawTile(); + + context.activeTexture.set(gl.TEXTURE0); + particleState.targetColorTexture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE); + + context.activeTexture.set(gl.TEXTURE1); + + let parentScaleBy, parentTL; + if (parentTile && parentTile.rasterParticleState) { + parentTile.rasterParticleState.targetColorTexture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE); + parentScaleBy = Math.pow(2, parentTile.tileID.overscaledZ - tile.tileID.overscaledZ); + parentTL = [tile.tileID.canonical.x * parentScaleBy % 1, tile.tileID.canonical.y * parentScaleBy % 1]; + } else { + particleState.targetColorTexture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE); + } + + const projMatrix = isGlobeProjection ? Float32Array.from(painter.transform.expandedFarZProjMatrix) : painter.transform.calculateProjMatrix(unwrappedTileID, align); + + const tr = painter.transform; + const cutoffParams = cutoffParamsForElevation(tr); + const tileBounds = tileCornersToBounds(coord.canonical); + const latitudinalLod = getLatitudinalLod(tileBounds.getCenter().lat); + + let normalizeMatrix: Float32Array; + let globeMatrix: Float32Array; + let globeMercatorMatrix: Float32Array; + let mercatorCenter: [number, number]; + let gridMatrix: Float32Array; + + if (isGlobeProjection) { + normalizeMatrix = Float32Array.from(globeNormalizeECEF(globeTileBounds(coord.canonical))); + globeMatrix = Float32Array.from(tr.globeMatrix); + globeMercatorMatrix = Float32Array.from(calculateGlobeMercatorMatrix(tr)); + mercatorCenter = [mercatorXfromLng(tr.center.lng), mercatorYfromLat(tr.center.lat)]; + gridMatrix = Float32Array.from(getGridMatrix(coord.canonical, tileBounds, latitudinalLod, tr.worldSize / tr._pixelsPerMercatorPixel)); + } else { + normalizeMatrix = new Float32Array(16); + globeMatrix = new Float32Array(9); + globeMercatorMatrix = new Float32Array(16); + mercatorCenter = [0, 0]; + gridMatrix = new Float32Array(9); + } + + const uniformValues = rasterParticleUniformValues( + projMatrix, + normalizeMatrix, + globeMatrix, + globeMercatorMatrix, + gridMatrix, + parentTL || [0, 0], + globeToMercatorTransition(painter.transform.zoom), + mercatorCenter, + cutoffParams, + parentScaleBy || 1, + fade, + rasterElevation + ); + const overrideFog = painter.isTileAffectedByFog(coord); + const program = painter.getOrCreateProgram('rasterParticle', {defines, overrideFog}); + + painter.uploadCommonUniforms(context, program, unwrappedTileID); + + if (isGlobeProjection) { + const depthMode = new DepthMode(gl.LEQUAL, DepthMode.ReadWrite, painter.depthRangeFor3D); + const skirtHeightValue = 0; + const sharedBuffers = painter.globeSharedBuffers; + if (sharedBuffers) { + const [buffer, indexBuffer, segments] = sharedBuffers.getGridBuffers(latitudinalLod, skirtHeightValue !== 0); + assert(buffer); + assert(indexBuffer); + assert(segments); + program.draw(painter, gl.TRIANGLES, depthMode, stencilMode, ColorMode.alphaBlended, CullFaceMode.backCCW, uniformValues, layer.id, buffer, indexBuffer, segments); + } + } else { + const depthMode = painter.depthModeForSublayer(0, DepthMode.ReadOnly); + const stencilMode = stencilModes[coord.overscaledZ]; + const {tileBoundsBuffer, tileBoundsIndexBuffer, tileBoundsSegments} = painter.getTileBoundsBuffers(tile); + + program.draw(painter, gl.TRIANGLES, depthMode, stencilMode, ColorMode.alphaBlended, CullFaceMode.disabled, + uniformValues, layer.id, tileBoundsBuffer, + tileBoundsIndexBuffer, tileBoundsSegments); + } + } + + painter.resetStencilClippingMasks(); +} + +function cutoffParamsForElevation(tr: Transform): [number, number, number, number] { + const near = tr._nearZ; + const far = tr.projection.farthestPixelDistance(tr); + const zRange = far - near; + const fadeRangePixels = tr.height * 0.2; + const cutoffDistance = near + fadeRangePixels; + const relativeCutoffDistance = ((cutoffDistance - near) / zRange); + const relativeCutoffFadeDistance = ((cutoffDistance - fadeRangePixels - near) / zRange); + return [near, far, relativeCutoffFadeDistance, relativeCutoffDistance]; +} + +export function prepare(layer: RasterParticleStyleLayer, sourceCache: SourceCache, _: Painter): void { + const source = sourceCache.getSource(); + if (!(source instanceof RasterArrayTileSource) || !source.loaded()) return; + + const sourceLayer = layer.sourceLayer || (source.rasterLayerIds && source.rasterLayerIds[0]); + if (!sourceLayer) return; + + const band = layer.paint.get('raster-particle-array-band') || source.getInitialBand(sourceLayer); + if (band == null) return; + + // $FlowFixMe[incompatible-type] + const tiles: Array = sourceCache.getIds().map(id => sourceCache.getTileByID(id)); + for (const tile of tiles) { + if (tile.updateNeeded(sourceLayer, band)) { + source.prepareTile(tile, sourceLayer, band); + } + } +} diff --git a/src/render/draw_sky.js b/src/render/draw_sky.js index 9cfcebb3401..3cc9d835c84 100644 --- a/src/render/draw_sky.js +++ b/src/render/draw_sky.js @@ -6,17 +6,17 @@ import ColorMode from '../gl/color_mode.js'; import CullFaceMode from '../gl/cull_face_mode.js'; import Texture from './texture.js'; import Program from './program.js'; -import type SourceCache from '../source/source_cache.js'; import SkyboxGeometry from './skybox_geometry.js'; import {skyboxUniformValues, skyboxGradientUniformValues} from './program/skybox_program.js'; import {skyboxCaptureUniformValues} from './program/skybox_capture_program.js'; import SkyLayer from '../style/style_layer/sky_style_layer.js'; -import type Painter from './painter.js'; import {mat3, mat4} from 'gl-matrix'; import assert from 'assert'; +import {globeToMercatorTransition} from '../geo/projection/globe_util.js'; +import type SourceCache from '../source/source_cache.js'; +import type Painter from './painter.js'; import type {Mat4} from 'gl-matrix'; -import {globeToMercatorTransition} from '../geo/projection/globe_util.js'; export default drawSky; diff --git a/src/render/draw_symbol.js b/src/render/draw_symbol.js index a3377098d98..5f6df12ac48 100644 --- a/src/render/draw_symbol.js +++ b/src/render/draw_symbol.js @@ -32,7 +32,8 @@ import {getSymbolTileProjectionMatrix} from '../geo/projection/projection_util.j import type Painter from './painter.js'; import type SourceCache from '../source/source_cache.js'; import type SymbolStyleLayer from '../style/style_layer/symbol_style_layer.js'; -import type SymbolBucket, {SymbolBuffers} from '../data/bucket/symbol_bucket.js'; +import type SymbolBucket from '../data/bucket/symbol_bucket.js'; +import type {SymbolBuffers} from '../data/bucket/symbol_bucket.js'; import Texture from '../render/texture.js'; import type ColorMode from '../gl/color_mode.js'; import {OverscaledTileID} from '../source/tile_id.js'; @@ -57,7 +58,7 @@ type SymbolTileRenderState = { hasHalo: boolean, tile: Tile, labelPlaneMatrixInv: ?Float32Array - } + } | null }; type Alignment = 'auto' | 'map' | 'viewport'; @@ -80,26 +81,21 @@ function drawSymbols(painter: Painter, sourceCache: SourceCache, layer: SymbolSt ); } - if (layer.paint.get('icon-opacity').constantOr(1) !== 0) { - drawLayerSymbols(painter, sourceCache, layer, coords, false, - layer.paint.get('icon-translate'), - layer.paint.get('icon-translate-anchor'), - layer.layout.get('icon-rotation-alignment'), - layer.layout.get('icon-pitch-alignment'), - layer.layout.get('icon-keep-upright'), - stencilMode, colorMode - ); - } + const areIconsVisible = layer.paint.get('icon-opacity').constantOr(1) !== 0; + const areTextsVisible = layer.paint.get('text-opacity').constantOr(1) !== 0; - if (layer.paint.get('text-opacity').constantOr(1) !== 0) { - drawLayerSymbols(painter, sourceCache, layer, coords, true, - layer.paint.get('text-translate'), - layer.paint.get('text-translate-anchor'), - layer.layout.get('text-rotation-alignment'), - layer.layout.get('text-pitch-alignment'), - layer.layout.get('text-keep-upright'), - stencilMode, colorMode - ); + // Support of ordering of symbols and texts comes with possible sacrificing of performance + // because of switching of shader program for every render state from icon to SDF. + // To address this problem, let's use one-phase rendering only when sort key provided + if (layer.layout.get('symbol-sort-key').constantOr(1) !== undefined && (areIconsVisible || areTextsVisible)) { + drawLayerSymbols(painter, sourceCache, layer, coords, stencilMode, colorMode); + } else { + if (areIconsVisible) { + drawLayerSymbols(painter, sourceCache, layer, coords, stencilMode, colorMode, {onlyIcons: true}); + } + if (areTextsVisible) { + drawLayerSymbols(painter, sourceCache, layer, coords, stencilMode, colorMode, {onlyText: true}); + } } if (sourceCache.map.showCollisionBoxes) { @@ -258,29 +254,43 @@ function updateVariableAnchorsForBucket(bucket: SymbolBucket, rotateWithMap: boo bucket.text.dynamicLayoutVertexBuffer.updateData(dynamicTextLayoutVertexArray); } -function getSymbolProgramName(isSDF: boolean, isText: boolean, bucket: SymbolBucket) { - if (bucket.iconsInText && isText) { - return 'symbolTextAndIcon'; - } else if (isSDF) { - return 'symbolSDF'; - } else { - return 'symbolIcon'; - } +type DrawLayerSymbolsOptions = { + onlyIcons?: boolean; + onlyText?: boolean; } -function drawLayerSymbols(painter: Painter, sourceCache: SourceCache, layer: SymbolStyleLayer, coords: Array, isText: boolean, translate: [number, number], translateAnchor: 'map' | 'viewport', rotationAlignment: Alignment, pitchAlignment: Alignment, keepUpright: boolean, stencilMode: StencilMode, colorMode: ColorMode) { +function drawLayerSymbols( + painter: Painter, + sourceCache: SourceCache, + layer: SymbolStyleLayer, + coords: Array, + stencilMode: StencilMode, + colorMode: ColorMode, + options: DrawLayerSymbolsOptions = {} +) { + const iconTranslate = layer.paint.get('icon-translate'); + const textTranslate = layer.paint.get('text-translate'); + const iconTranslateAnchor = layer.paint.get('icon-translate-anchor'); + const textTranslateAnchor = layer.paint.get('text-translate-anchor'); + const iconRotationAlignment = layer.layout.get('icon-rotation-alignment'); + const textRotationAlignment = layer.layout.get('text-rotation-alignment'); + const iconPitchAlignment = layer.layout.get('icon-pitch-alignment'); + const textPitchAlignment = layer.layout.get('text-pitch-alignment'); + const iconKeepUpright = layer.layout.get('icon-keep-upright'); + const textKeepUpright = layer.layout.get('text-keep-upright'); + const iconSaturation = layer.paint.get('icon-color-saturation'); + const iconContrast = layer.paint.get('icon-color-contrast'); + const iconBrightnessMin = layer.paint.get('icon-color-brightness-min'); + const iconBrightnessMax = layer.paint.get('icon-color-brightness-max'); + const context = painter.context; const gl = context.gl; const tr = painter.transform; - const rotateWithMap = rotationAlignment === 'map'; - const pitchWithMap = pitchAlignment === 'map'; - const alongLine = rotateWithMap && layer.layout.get('symbol-placement') !== 'point'; - - // Line label rotation happens in `updateLineLabels` - // Pitched point labels are automatically rotated by the labelPlaneMatrix projection - // Unpitched point labels need to have their rotation applied after projection - const rotateInShader = rotateWithMap && !pitchWithMap && !alongLine; + const iconRotateWithMap = iconRotationAlignment === 'map'; + const textRotateWithMap = textRotationAlignment === 'map'; + const iconPitchWithMap = iconPitchAlignment === 'map'; + const textPitchWithMap = textPitchAlignment === 'map'; const hasSortKey = layer.layout.get('symbol-sort-key').constantOr(1) !== undefined; let sortFeaturesByKey = false; @@ -296,15 +306,6 @@ function drawLayerSymbols(painter: Painter, sourceCache: SourceCache, layer: Sym const mercatorCameraUp = [0, -1, 0]; - let globeCameraUp: [number, number, number] = mercatorCameraUp; - if ((isGlobeProjection || tr.mercatorFromTransition) && !rotateWithMap) { - // Each symbol rotating with the viewport requires per-instance information about - // how to align with the viewport. In 2D case rotation is shared between all of the symbols and - // hence embedded in the label plane matrix but in globe view this needs to be computed at runtime. - // Camera up vector together with surface normals can be used to find the correct orientation for each symbol. - globeCameraUp = computeGlobeCameraUp(tr); - } - for (const coord of coords) { const tile = sourceCache.getTile(coord); const bucket: SymbolBucket = (tile.getBucket(layer): any); @@ -314,138 +315,258 @@ function drawLayerSymbols(painter: Painter, sourceCache: SourceCache, layer: Sym if (bucket.projection.name === 'mercator' && isGlobeProjection) { continue; } - const buffers = isText ? bucket.text : bucket.icon; - if (!buffers || bucket.fullyClipped || !buffers.segments.get().length) continue; - const programConfiguration = buffers.programConfigurations.get(layer.id); - const isSDF = isText || bucket.sdfIcons; - - const sizeData = isText ? bucket.textSizeData : bucket.iconSizeData; - const transformed = pitchWithMap || tr.pitch !== 0; - - const size = symbolSize.evaluateSizeForZoom(sizeData, tr.zoom); - - let texSize: [number, number]; - let texSizeIcon: [number, number] = [0, 0]; - let atlasTexture: Texture | null; - let atlasInterpolation; - let atlasTextureIcon: Texture | null = null; - let atlasInterpolationIcon; - if (isText) { - atlasTexture = tile.glyphAtlasTexture ? tile.glyphAtlasTexture : null; - atlasInterpolation = gl.LINEAR; - texSize = tile.glyphAtlasTexture ? tile.glyphAtlasTexture.size : [0, 0]; - if (bucket.iconsInText) { - texSizeIcon = tile.imageAtlasTexture ? tile.imageAtlasTexture.size : [0, 0]; - atlasTextureIcon = tile.imageAtlasTexture ? tile.imageAtlasTexture : null; - const zoomDependentSize = sizeData.kind === 'composite' || sizeData.kind === 'camera'; - atlasInterpolationIcon = transformed || painter.options.rotating || painter.options.zooming || zoomDependentSize ? gl.LINEAR : gl.NEAREST; - } - } else { - const iconScaled = layer.layout.get('icon-size').constantOr(0) !== 1 || bucket.iconsNeedLinear; - atlasTexture = tile.imageAtlasTexture ? tile.imageAtlasTexture : null; - atlasInterpolation = isSDF || painter.options.rotating || painter.options.zooming || iconScaled || transformed ? - gl.LINEAR : - gl.NEAREST; - texSize = tile.imageAtlasTexture ? tile.imageAtlasTexture.size : [0, 0]; - } + if (bucket.fullyClipped) continue; const bucketIsGlobeProjection = bucket.projection.name === 'globe'; - const cameraUpVector = bucketIsGlobeProjection ? globeCameraUp : mercatorCameraUp; const globeToMercator = bucketIsGlobeProjection ? globeToMercatorTransition(tr.zoom) : 0.0; const tileMatrix = getSymbolTileProjectionMatrix(coord, bucket.getProjection(), tr); const s = tr.calculatePixelsToTileUnitsMatrix(tile); - const labelPlaneMatrixRendering = symbolProjection.getLabelPlaneMatrixForRendering(tileMatrix, tile.tileID.canonical, pitchWithMap, rotateWithMap, tr, bucket.getProjection(), s); - // labelPlaneMatrixInv is used for converting vertex pos to tile coordinates needed for sampling elevation. - const labelPlaneMatrixInv = painter.terrain && pitchWithMap && alongLine ? mat4.invert(mat4.create(), labelPlaneMatrixRendering) : identityMat4; - const glCoordMatrix = symbolProjection.getGlCoordMatrix(tileMatrix, tile.tileID.canonical, pitchWithMap, rotateWithMap, tr, bucket.getProjection(), s); const hasVariableAnchors = variablePlacement && bucket.hasTextData(); const updateTextFitIcon = bucket.hasIconTextFit() && hasVariableAnchors && bucket.hasIconData(); - if (alongLine) { - const elevation = tr.elevation; - const getElevation = elevation ? elevation.getAtTileOffsetFunc(coord, tr.center.lat, tr.worldSize, bucket.getProjection()) : null; - const labelPlaneMatrixPlacement = symbolProjection.getLabelPlaneMatrixForPlacement(tileMatrix, tile.tileID.canonical, pitchWithMap, rotateWithMap, tr, bucket.getProjection(), s); + const invMatrix = bucket.getProjection().createInversionMatrix(tr, coord.canonical); - symbolProjection.updateLineLabels(bucket, tileMatrix, painter, isText, labelPlaneMatrixPlacement, glCoordMatrix, pitchWithMap, keepUpright, getElevation, coord); - } + const getIconState = () => { + const alongLine = iconRotateWithMap && layer.layout.get('symbol-placement') !== 'point'; - const projectedPosOnLabelSpace = alongLine || (isText && variablePlacement) || updateTextFitIcon; - const matrix = painter.translatePosMatrix(tileMatrix, tile, translate, translateAnchor); - const uLabelPlaneMatrix = projectedPosOnLabelSpace ? identityMat4 : labelPlaneMatrixRendering; - const uglCoordMatrix = painter.translatePosMatrix(glCoordMatrix, tile, translate, translateAnchor, true); - const invMatrix = bucket.getProjection().createInversionMatrix(tr, coord.canonical); - const transitionProgress = layer.paint.get('icon-image-cross-fade').constantOr(0.0); + const baseDefines = ([]: any); + const projectedPosOnLabelSpace = alongLine || updateTextFitIcon; + const transitionProgress = layer.paint.get('icon-image-cross-fade').constantOr(0.0); + if (painter.terrainRenderModeElevated() && iconPitchWithMap) { + baseDefines.push('PITCH_WITH_MAP_TERRAIN'); + } + if (bucketIsGlobeProjection) { + baseDefines.push('PROJECTION_GLOBE_VIEW'); + if (projectedPosOnLabelSpace) { + baseDefines.push('PROJECTED_POS_ON_VIEWPORT'); + } + } + if (transitionProgress > 0.0) { + baseDefines.push('ICON_TRANSITION'); + } + if (bucket.icon.zOffsetVertexBuffer) { + baseDefines.push('Z_OFFSET'); + } - const baseDefines = ([]: any); - if (painter.terrainRenderModeElevated() && pitchWithMap) { - baseDefines.push('PITCH_WITH_MAP_TERRAIN'); - } - if (bucketIsGlobeProjection) { - baseDefines.push('PROJECTION_GLOBE_VIEW'); - if (projectedPosOnLabelSpace) { - baseDefines.push('PROJECTED_POS_ON_VIEWPORT'); + if (iconSaturation !== 0 || iconContrast !== 0 || iconBrightnessMin !== 0 || iconBrightnessMax !== 1) { + baseDefines.push('COLOR_ADJUSTMENT'); } - } - if (transitionProgress > 0.0) { - baseDefines.push('ICON_TRANSITION'); - } - if (buffers.zOffsetVertexBuffer) { - baseDefines.push('Z_OFFSET'); - } - const hasHalo = isSDF && layer.paint.get(isText ? 'text-halo-width' : 'icon-halo-width').constantOr(1) !== 0; + const programConfiguration = bucket.icon.programConfigurations.get(layer.id); + const program = painter.getOrCreateProgram(bucket.sdfIcons ? 'symbolSDF' : 'symbolIcon', {config: programConfiguration, defines: baseDefines}); + + let uniformValues; + const texSize = tile.imageAtlasTexture ? tile.imageAtlasTexture.size : [0, 0]; + const sizeData = bucket.iconSizeData; + const size = symbolSize.evaluateSizeForZoom(sizeData, tr.zoom); + const transformed = iconPitchWithMap || tr.pitch !== 0; + + const labelPlaneMatrixRendering = symbolProjection.getLabelPlaneMatrixForRendering(tileMatrix, tile.tileID.canonical, iconPitchWithMap, iconRotateWithMap, tr, bucket.getProjection(), s); + // labelPlaneMatrixInv is used for converting vertex pos to tile coordinates needed for sampling elevation. + const glCoordMatrix = symbolProjection.getGlCoordMatrix(tileMatrix, tile.tileID.canonical, iconPitchWithMap, iconRotateWithMap, tr, bucket.getProjection(), s); + const uglCoordMatrix = painter.translatePosMatrix(glCoordMatrix, tile, iconTranslate, iconTranslateAnchor, true); + const matrix = painter.translatePosMatrix(tileMatrix, tile, iconTranslate, iconTranslateAnchor); + const uLabelPlaneMatrix = projectedPosOnLabelSpace ? identityMat4 : labelPlaneMatrixRendering; + const rotateInShader = iconRotateWithMap && !iconPitchWithMap && !alongLine; + + let globeCameraUp: [number, number, number] = mercatorCameraUp; + if ((isGlobeProjection || tr.mercatorFromTransition) && !iconRotateWithMap) { + // Each symbol rotating with the viewport requires per-instance information about + // how to align with the viewport. In 2D case rotation is shared between all of the symbols and + // hence embedded in the label plane matrix but in globe view this needs to be computed at runtime. + // Camera up vector together with surface normals can be used to find the correct orientation for each symbol. + globeCameraUp = computeGlobeCameraUp(tr); + } + + const cameraUpVector = bucketIsGlobeProjection ? globeCameraUp : mercatorCameraUp; + + if (bucket.sdfIcons && !bucket.iconsInText) { + uniformValues = symbolSDFUniformValues(sizeData.kind, size, rotateInShader, iconPitchWithMap, painter, + matrix, uLabelPlaneMatrix, uglCoordMatrix, false, texSize, true, coord, globeToMercator, mercatorCenter, invMatrix, cameraUpVector, bucket.getProjection()); + } else { + const colorAdjustmentMatrix = layer.getColorAdjustmentMatrix(iconSaturation, iconContrast, iconBrightnessMin, iconBrightnessMax); + uniformValues = symbolIconUniformValues(sizeData.kind, size, rotateInShader, iconPitchWithMap, painter, matrix, + uLabelPlaneMatrix, uglCoordMatrix, false, texSize, coord, globeToMercator, mercatorCenter, invMatrix, cameraUpVector, bucket.getProjection(), colorAdjustmentMatrix, transitionProgress); + } + + const atlasTexture = tile.imageAtlasTexture ? tile.imageAtlasTexture : null; + const iconScaled = layer.layout.get('icon-size').constantOr(0) !== 1 || bucket.iconsNeedLinear; + const atlasInterpolation = bucket.sdfIcons || painter.options.rotating || painter.options.zooming || iconScaled || transformed ? gl.LINEAR : gl.NEAREST; + const hasHalo = bucket.sdfIcons && layer.paint.get('icon-halo-width').constantOr(1) !== 0; + const labelPlaneMatrixInv = painter.terrain && iconPitchWithMap && alongLine ? mat4.invert(mat4.create(), labelPlaneMatrixRendering) : identityMat4; + + // Line label rotation happens in `updateLineLabels` + // Pitched point labels are automatically rotated by the labelPlaneMatrix projection + // Unpitched point labels need to have their rotation applied after projection + + if (alongLine && bucket.icon) { + const elevation = tr.elevation; + const getElevation = elevation ? elevation.getAtTileOffsetFunc(coord, tr.center.lat, tr.worldSize, bucket.getProjection()) : null; + const labelPlaneMatrixPlacement = symbolProjection.getLabelPlaneMatrixForPlacement(tileMatrix, tile.tileID.canonical, iconPitchWithMap, iconRotateWithMap, tr, bucket.getProjection(), s); + + symbolProjection.updateLineLabels(bucket, tileMatrix, painter, false, labelPlaneMatrixPlacement, glCoordMatrix, iconPitchWithMap, iconKeepUpright, getElevation, coord); + } + + return { + program, + buffers: bucket.icon, + uniformValues, + atlasTexture, + atlasTextureIcon: null, + atlasInterpolation, + atlasInterpolationIcon: null, + isSDF: bucket.sdfIcons, + hasHalo, + tile, + labelPlaneMatrixInv, + }; + }; + + const getTextState = () => { + const alongLine = textRotateWithMap && layer.layout.get('symbol-placement') !== 'point'; + const baseDefines = ([]: any); + const projectedPosOnLabelSpace = alongLine || variablePlacement || updateTextFitIcon; + if (painter.terrainRenderModeElevated() && textPitchWithMap) { + baseDefines.push('PITCH_WITH_MAP_TERRAIN'); + } + if (bucketIsGlobeProjection) { + baseDefines.push('PROJECTION_GLOBE_VIEW'); + if (projectedPosOnLabelSpace) { + baseDefines.push('PROJECTED_POS_ON_VIEWPORT'); + } + } + if (bucket.text.zOffsetVertexBuffer) { + baseDefines.push('Z_OFFSET'); + } + + const programConfiguration = bucket.text.programConfigurations.get(layer.id); + const program = painter.getOrCreateProgram(bucket.iconsInText ? 'symbolTextAndIcon' : 'symbolSDF', {config: programConfiguration, defines: baseDefines}); + + let texSizeIcon: [number, number] = [0, 0]; + let atlasTextureIcon: Texture | null = null; + let atlasInterpolationIcon; + + const sizeData = bucket.textSizeData; + + if (bucket.iconsInText) { + texSizeIcon = tile.imageAtlasTexture ? tile.imageAtlasTexture.size : [0, 0]; + atlasTextureIcon = tile.imageAtlasTexture ? tile.imageAtlasTexture : null; + const transformed = textPitchWithMap || tr.pitch !== 0; + const zoomDependentSize = sizeData.kind === 'composite' || sizeData.kind === 'camera'; + atlasInterpolationIcon = transformed || painter.options.rotating || painter.options.zooming || zoomDependentSize ? gl.LINEAR : gl.NEAREST; + } + + const texSize = tile.glyphAtlasTexture ? tile.glyphAtlasTexture.size : [0, 0]; + const size = symbolSize.evaluateSizeForZoom(sizeData, tr.zoom); + const labelPlaneMatrixRendering = symbolProjection.getLabelPlaneMatrixForRendering(tileMatrix, tile.tileID.canonical, textPitchWithMap, textRotateWithMap, tr, bucket.getProjection(), s); + // labelPlaneMatrixInv is used for converting vertex pos to tile coordinates needed for sampling elevation. + const glCoordMatrix = symbolProjection.getGlCoordMatrix(tileMatrix, tile.tileID.canonical, textPitchWithMap, textRotateWithMap, tr, bucket.getProjection(), s); + const uglCoordMatrix = painter.translatePosMatrix(glCoordMatrix, tile, textTranslate, textTranslateAnchor, true); + const matrix = painter.translatePosMatrix(tileMatrix, tile, textTranslate, textTranslateAnchor); + const uLabelPlaneMatrix = projectedPosOnLabelSpace ? identityMat4 : labelPlaneMatrixRendering; + + // Line label rotation happens in `updateLineLabels` + // Pitched point labels are automatically rotated by the labelPlaneMatrix projection + // Unpitched point labels need to have their rotation applied after projection + const rotateInShader = textRotateWithMap && !textPitchWithMap && !alongLine; + + let globeCameraUp: [number, number, number] = mercatorCameraUp; + if ((isGlobeProjection || tr.mercatorFromTransition) && !textRotateWithMap) { + // Each symbol rotating with the viewport requires per-instance information about + // how to align with the viewport. In 2D case rotation is shared between all of the symbols and + // hence embedded in the label plane matrix but in globe view this needs to be computed at runtime. + // Camera up vector together with surface normals can be used to find the correct orientation for each symbol. + globeCameraUp = computeGlobeCameraUp(tr); + } + + const cameraUpVector = bucketIsGlobeProjection ? globeCameraUp : mercatorCameraUp; + + let uniformValues; - let uniformValues; - if (isSDF) { if (!bucket.iconsInText) { - uniformValues = symbolSDFUniformValues(sizeData.kind, size, rotateInShader, pitchWithMap, painter, - matrix, uLabelPlaneMatrix, uglCoordMatrix, isText, texSize, true, coord, globeToMercator, mercatorCenter, invMatrix, cameraUpVector, bucket.getProjection()); + uniformValues = symbolSDFUniformValues(sizeData.kind, size, rotateInShader, textPitchWithMap, painter, + matrix, uLabelPlaneMatrix, uglCoordMatrix, true, texSize, true, coord, globeToMercator, mercatorCenter, invMatrix, cameraUpVector, bucket.getProjection()); } else { - uniformValues = symbolTextAndIconUniformValues(sizeData.kind, size, rotateInShader, pitchWithMap, painter, + uniformValues = symbolTextAndIconUniformValues(sizeData.kind, size, rotateInShader, textPitchWithMap, painter, matrix, uLabelPlaneMatrix, uglCoordMatrix, texSize, texSizeIcon, coord, globeToMercator, mercatorCenter, invMatrix, cameraUpVector, bucket.getProjection()); } - } else { - uniformValues = symbolIconUniformValues(sizeData.kind, size, rotateInShader, pitchWithMap, painter, matrix, - uLabelPlaneMatrix, uglCoordMatrix, isText, texSize, coord, globeToMercator, mercatorCenter, invMatrix, cameraUpVector, bucket.getProjection(), transitionProgress); - } - const program = painter.getOrCreateProgram(getSymbolProgramName(isSDF, isText, bucket), {config: programConfiguration, defines: baseDefines}); - - const state = { - program, - buffers, - uniformValues, - atlasTexture, - atlasTextureIcon, - atlasInterpolation, - atlasInterpolationIcon, - isSDF, - hasHalo, - tile, - labelPlaneMatrixInv + const atlasTexture = tile.glyphAtlasTexture ? tile.glyphAtlasTexture : null; + const atlasInterpolation = gl.LINEAR; + const hasHalo = layer.paint.get('text-halo-width').constantOr(1) !== 0; + const labelPlaneMatrixInv = painter.terrain && textPitchWithMap && alongLine ? mat4.invert(mat4.create(), labelPlaneMatrixRendering) : identityMat4; + + if (alongLine && bucket.text) { + const elevation = tr.elevation; + const getElevation = elevation ? elevation.getAtTileOffsetFunc(coord, tr.center.lat, tr.worldSize, bucket.getProjection()) : null; + const labelPlaneMatrixPlacement = symbolProjection.getLabelPlaneMatrixForPlacement(tileMatrix, tile.tileID.canonical, textPitchWithMap, textRotateWithMap, tr, bucket.getProjection(), s); + + symbolProjection.updateLineLabels(bucket, tileMatrix, painter, true, labelPlaneMatrixPlacement, glCoordMatrix, textPitchWithMap, textKeepUpright, getElevation, coord); + } + + return { + program, + buffers: bucket.text, + uniformValues, + atlasTexture, + atlasTextureIcon, + atlasInterpolation, + atlasInterpolationIcon, + isSDF: true, + hasHalo, + tile, + labelPlaneMatrixInv, + }; }; + const iconSegmentsLength = bucket.icon.segments.get().length; + const textSegmentsLength = bucket.text.segments.get().length; + const iconState = iconSegmentsLength && !options.onlyText ? getIconState() : null; + const textState = textSegmentsLength && !options.onlyIcons ? getTextState() : null; + const iconOpacity = layer.paint.get('icon-opacity').constantOr(1.0); + const textOpacity = layer.paint.get('text-opacity').constantOr(1.0); + if (hasSortKey && bucket.canOverlap) { sortFeaturesByKey = true; - const oldSegments = buffers.segments.get(); - for (const segment of oldSegments) { + const oldIconSegments = iconOpacity && !options.onlyText ? bucket.icon.segments.get() : []; + const oldTextSegments = textOpacity && !options.onlyIcons ? bucket.text.segments.get() : []; + + for (const segment of oldIconSegments) { tileRenderState.push({ segments: new SegmentVector([segment]), sortKey: ((segment.sortKey: any): number), - state + state: iconState + }); + } + + for (const segment of oldTextSegments) { + tileRenderState.push({ + segments: new SegmentVector([segment]), + sortKey: ((segment.sortKey: any): number), + state: textState }); } } else { - tileRenderState.push({ - segments: buffers.segments, - sortKey: 0, - state - }); + if (!options.onlyText) { + tileRenderState.push({ + segments: iconOpacity ? bucket.icon.segments : new SegmentVector([]), + sortKey: 0, + state: iconState + }); + } + + if (!options.onlyIcons) { + tileRenderState.push({ + segments: textOpacity ? bucket.text.segments : new SegmentVector([]), + sortKey: 0, + state: textState + }); + } } } @@ -455,6 +576,11 @@ function drawLayerSymbols(painter: Painter, sourceCache: SourceCache, layer: Sym for (const segmentState of tileRenderState) { const state = segmentState.state; + + if (!state) { + continue; + } + if (painter.terrain) { const options = { useDepthForOcclusion: tr.depthOcclusionForSymbolsAndCircles, @@ -464,12 +590,12 @@ function drawLayerSymbols(painter: Painter, sourceCache: SourceCache, layer: Sym } context.activeTexture.set(gl.TEXTURE0); if (state.atlasTexture) { - state.atlasTexture.bind(state.atlasInterpolation, gl.CLAMP_TO_EDGE); + state.atlasTexture.bind(state.atlasInterpolation, gl.CLAMP_TO_EDGE, true); } if (state.atlasTextureIcon) { context.activeTexture.set(gl.TEXTURE1); if (state.atlasTextureIcon) { - state.atlasTextureIcon.bind(state.atlasInterpolationIcon, gl.CLAMP_TO_EDGE); + state.atlasTextureIcon.bind(state.atlasInterpolationIcon, gl.CLAMP_TO_EDGE, true); } } diff --git a/src/render/fog.js b/src/render/fog.js index ff4eea79d37..ebedc01cb7b 100644 --- a/src/render/fog.js +++ b/src/render/fog.js @@ -1,13 +1,14 @@ // @flow import Context from '../gl/context.js'; -import type {UniformValues} from './uniform_binding.js'; -import type {UnwrappedTileID} from '../source/tile_id.js'; -import Painter from './painter.js'; import Fog from '../style/fog.js'; import {Uniform1f, Uniform1i, Uniform2f, Uniform3f, Uniform4f, UniformMatrix4f} from './uniform_binding.js'; import {globeToMercatorTransition} from '../geo/projection/globe_util.js'; +import type {UniformValues} from './uniform_binding.js'; +import type {UnwrappedTileID} from '../source/tile_id.js'; +import type Painter from './painter.js'; + export type FogUniformsType = {| 'u_fog_matrix': UniformMatrix4f, 'u_fog_range': Uniform2f, diff --git a/src/render/glyph_manager.js b/src/render/glyph_manager.js index 940f9130d30..ee6f890972d 100644 --- a/src/render/glyph_manager.js +++ b/src/render/glyph_manager.js @@ -8,6 +8,7 @@ import config from '../util/config.js'; import {asyncAll} from '../util/util.js'; import {AlphaImage} from '../util/image.js'; +import type {Class} from '../types/class.js'; import type {StyleGlyph} from '../style/style_glyph.js'; import type {RequestManager} from '../util/mapbox.js'; import type {Callback} from '../types/callback.js'; diff --git a/src/render/image_atlas.js b/src/render/image_atlas.js index dd3a0b7a151..d047edcaafc 100644 --- a/src/render/image_atlas.js +++ b/src/render/image_atlas.js @@ -80,7 +80,10 @@ export default class ImageAtlas { for (const id in icons) { const src = icons[id]; const bin = iconPositions[id].paddedRect; - RGBAImage.copy(src.data, image, {x: 0, y: 0}, {x: bin.x + IMAGE_PADDING, y: bin.y + IMAGE_PADDING}, src.data); + // For SDF icons, we override the RGB channels with white. + // This is because we read the red channel in the shader and RGB channels will get alpha-premultiplied on upload. + const overrideRGB = src.sdf; + RGBAImage.copy(src.data, image, {x: 0, y: 0}, {x: bin.x + IMAGE_PADDING, y: bin.y + IMAGE_PADDING}, src.data, overrideRGB); } for (const id in patterns) { @@ -138,7 +141,8 @@ export default class ImageAtlas { position.version = image.version; const [x, y] = position.tl; - texture.update(image.data, undefined, {x, y}); + const hasPattern = !!Object.keys(this.patternPositions).length; + texture.update(image.data, {useMipmap: hasPattern}, {x, y}); } } diff --git a/src/render/image_manager.js b/src/render/image_manager.js index 51e13e664b5..87402bd13b4 100644 --- a/src/render/image_manager.js +++ b/src/render/image_manager.js @@ -12,12 +12,12 @@ import {warnOnce} from '../util/util.js'; import type {StyleImage} from '../style/style_image.js'; import type Context from '../gl/context.js'; -import type {Bin} from 'potpack'; +import type {Bin as PotpackBox} from 'potpack'; import type {Callback} from '../types/callback.js'; import type {Size} from '../util/image.js'; type Pattern = { - bin: Bin, + bin: PotpackBox, position: ImagePosition }; diff --git a/src/render/painter.js b/src/render/painter.js index ebb4f9683cb..2a4df45b03a 100644 --- a/src/render/painter.js +++ b/src/render/painter.js @@ -1,7 +1,6 @@ // @flow import browser from '../util/browser.js'; -import window from '../util/window.js'; import {mat4} from 'gl-matrix'; import SourceCache from '../source/source_cache.js'; @@ -33,7 +32,8 @@ import line from './draw_line.js'; import fill from './draw_fill.js'; import fillExtrusion from './draw_fill_extrusion.js'; import hillshade from './draw_hillshade.js'; -import raster from './draw_raster.js'; +import raster, {prepare as prepareRaster} from './draw_raster.js'; +import rasterParticle, {prepare as prepareRasterParticle} from './draw_raster_particle.js'; import background from './draw_background.js'; import debug, {drawDebugPadding, drawDebugQueryGeometry} from './draw_debug.js'; import custom from './draw_custom.js'; @@ -49,33 +49,11 @@ import type {Source} from '../source/source.js'; import type {CutoffParams} from '../render/cutoff.js'; // 3D-style related -import model, {upload as modelUpload} from '../../3d-style/render/draw_model.js'; +import model, {prepare as modelPrepare} from '../../3d-style/render/draw_model.js'; import {lightsUniformValues} from '../../3d-style/render/lights.js'; - import {ShadowRenderer} from '../../3d-style/render/shadow_renderer.js'; - import {WireframeDebugCache} from "./wireframe_cache.js"; -const draw = { - symbol, - circle, - heatmap, - line, - fill, - 'fill-extrusion': fillExtrusion, - hillshade, - raster, - background, - sky, - debug, - custom, - model -}; - -const upload = { - modelUpload -}; - import type Transform from '../geo/transform.js'; import type {OverscaledTileID, UnwrappedTileID} from '../source/tile_id.js'; import type Style from '../style/style.js'; @@ -89,6 +67,8 @@ import type {DepthRangeType, DepthMaskType, DepthFuncType} from '../gl/types.js' import type ResolvedImage from '../style-spec/expression/types/resolved_image.js'; import type {DynamicDefinesType} from './program/program_uniforms.js'; import {FOG_OPACITY_THRESHOLD} from '../style/fog_helpers.js'; +import type {ContextOptions} from '../gl/context.js'; +import type {ITrackedParameters} from 'tracked_parameters_proxy'; export type RenderPass = 'offscreen' | 'opaque' | 'translucent' | 'sky' | 'shadow' | 'light-beam'; export type CanvasCopyInstances = { @@ -112,6 +92,7 @@ type WireframeOptions = { type PainterOptions = { showOverdrawInspector: boolean, showTileBoundaries: boolean, + showParseStatus: boolean, showQueryGeometry: boolean, showTileAABBs: boolean, showPadding: boolean, @@ -134,6 +115,29 @@ type TileBoundsBuffers = {| type GPUTimers = {[layerId: string]: any}; +const draw = { + symbol, + circle, + heatmap, + line, + fill, + 'fill-extrusion': fillExtrusion, + hillshade, + raster, + 'raster-particle': rasterParticle, + background, + sky, + debug, + custom, + model +}; + +const prepare = { + model: modelPrepare, + raster: prepareRaster, + 'raster-particle': prepareRasterParticle +}; + /** * Initialize a new painter object. * @@ -199,6 +203,7 @@ class Painter { minCutoffZoom: number; renderDefaultNorthPole: boolean; renderDefaultSouthPole: boolean; + renderElevatedRasterBackface: boolean; _fogVisible: boolean; _cachedTileFogOpacities: {[number]: [number, number]}; @@ -206,12 +211,56 @@ class Painter { _wireframeDebugCache: WireframeDebugCache; - constructor(gl: WebGL2RenderingContext, transform: Transform) { - this.context = new Context(gl); + tp: ITrackedParameters; + + _debugParams: { + showTerrainProxyTiles: boolean; + fpsWindow: number; + continousRedraw: boolean; + } + + _timeStamp: number; + _averageFPS: number; + + _fpsHistory: Array; + + constructor(gl: WebGL2RenderingContext, contextCreateOptions: ContextOptions, transform: Transform, tp: ITrackedParameters) { + this.context = new Context(gl, contextCreateOptions); this.transform = transform; this._tileTextures = {}; this.frameCopies = []; this.loadTimeStamps = []; + this.tp = tp; + this._timeStamp = new Date().getTime(); + this._averageFPS = 0; + this._fpsHistory = []; + + this._debugParams = { + showTerrainProxyTiles: false, + fpsWindow: 30, + continousRedraw:false, + }; + + tp.registerParameter(this._debugParams, ["Terrain"], "showTerrainProxyTiles", {}, () => { + this.style.map.triggerRepaint(); + }); + + tp.registerParameter(this._debugParams, ["FPS"], "fpsWindow", {min: 1, max: 100, step: 1}); + tp.registerBinding(this._debugParams, ["FPS"], 'continousRedraw', { + readonly:true, + label: "continuous redraw" + }); + tp.registerBinding(this, ["FPS"], '_averageFPS', { + readonly:true, + label: "value" + }); + tp.registerBinding(this, ["FPS"], '_averageFPS', { + readonly:true, + label: "graph", + view:'graph', + min: 0, + max: 200 + }); this.setup(); @@ -241,6 +290,7 @@ class Painter { updateTerrain(style: Style, adaptCameraAltitude: boolean) { const enabled = !!style && !!style.terrain && this.transform.projection.supportsTerrain; if (!enabled && (!this._terrain || !this._terrain.enabled)) return; + if (!this._terrain) { this._terrain = new Terrain(this, style); } @@ -358,7 +408,7 @@ class Painter { const gl = this.context.gl; this.stencilClearMode = new StencilMode({func: gl.ALWAYS, mask: 0}, 0x0, 0xFF, gl.ZERO, gl.ZERO, gl.ZERO); - this.loadTimeStamps.push(window.performance.now()); + this.loadTimeStamps.push(performance.now()); } getMercatorTileBoundsBuffers(): TileBoundsBuffers { @@ -554,10 +604,28 @@ class Painter { return this.currentLayer < this.opaquePassCutoff; } + updateAverageFPS() { + const curTime = new Date().getTime(); + const dt = curTime - this._timeStamp; + this._timeStamp = curTime; + + const fps = dt === 0 ? 0 : 1000.0 / dt; + + this._fpsHistory.push(fps); + if (this._fpsHistory.length > this._debugParams.fpsWindow) { + this._fpsHistory.splice(0, this._fpsHistory.length - this._debugParams.fpsWindow); + } + + this._averageFPS = Math.round(this._fpsHistory.reduce((accum: number, current: number) => { return accum + current / this._fpsHistory.length; }, 0)); + } + render(style: Style, options: PainterOptions) { + Debug.run(() => { this.updateAverageFPS(); }); + // Update debug cache, i.e. clear all unused buffers this._wireframeDebugCache.update(this.frameCounter); + this._debugParams.continousRedraw = style.map.repaint; this.style = style; this.options = options; @@ -587,6 +655,11 @@ class Painter { } } + for (const layer of orderedLayers) { + if (layer.isHidden(this.transform.zoom)) continue; + this.prepareLayer(layer); + } + const coordsAscending: {[_: string]: Array} = {}; const coordsDescending: {[_: string]: Array} = {}; const coordsDescendingSymbol: {[_: string]: Array} = {}; @@ -604,11 +677,10 @@ class Painter { const getLayerSource = (layer: StyleLayer) => { const cache = this.style.getLayerSourceCache(layer); - if (!cache || !cache.used) { - return null; - } + if (!cache || !cache.used) return null; return cache.getSource(); }; + if (conflationSourcesInStyle) { const conflationLayersInStyle = []; @@ -724,16 +796,9 @@ class Painter { this.globeSharedBuffers = new GlobeSharedBuffers(this.context); } - // upload pass - for (const layer of orderedLayers) { - if (layer.isHidden(this.transform.zoom)) continue; - const sourceCache = style.getLayerSourceCache(layer); - this.uploadLayer(this, layer, sourceCache); - } - if (this.style.fog && this.transform.projection.supportsFog) { if (!this._atmosphere) { - this._atmosphere = new Atmosphere(); + this._atmosphere = new Atmosphere(this); } this._atmosphere.update(this); @@ -758,7 +823,7 @@ class Painter { if (!layer.hasOffscreenPass() || layer.isHidden(this.transform.zoom)) continue; const coords = sourceCache ? coordsDescending[sourceCache.id] : undefined; - if (!(layer.type === 'custom' || layer.type === 'raster' || layer.isSky()) && !(coords && coords.length)) continue; + if (!(layer.type === 'custom' || layer.type === 'raster' || layer.type === 'raster-particle' || layer.isSky()) && !(coords && coords.length)) continue; this.renderLayer(this, sourceCache, layer, coords); } @@ -858,6 +923,37 @@ class Painter { // Draw all other layers bottom-to-top. this.renderPass = 'translucent'; + function coordsForTranslucentLayer(layer: StyleLayer, sourceCache?: SourceCache) { + // For symbol layers in the translucent pass, we add extra tiles to the renderable set + // for cross-tile symbol fading. Symbol layers don't use tile clipping, so no need to render + // separate clipping masks + let coords: ?Array; + + if (sourceCache) { + const coordsSet = layer.type === 'symbol' ? coordsDescendingSymbol : + (layer.is3D() ? coordsSortedByDistance : coordsDescending); + + coords = coordsSet[sourceCache.id]; + } + return coords; + } + + // Render elevated raster layers behind the globe + const isGlobe = this.transform.projection.name === 'globe'; + if (isGlobe) { + this.renderElevatedRasterBackface = true; + this.currentLayer = 0; + while (this.currentLayer < layerIds.length) { + const layer = orderedLayers[this.currentLayer]; + if (layer.type === "raster") { + const sourceCache = style.getLayerSourceCache(layer); + this.renderLayer(this, sourceCache, layer, coordsForTranslucentLayer(layer, sourceCache)); + } + ++this.currentLayer; + } + this.renderElevatedRasterBackface = false; + } + this.currentLayer = 0; this.firstLightBeamLayer = Number.MAX_SAFE_INTEGER; @@ -891,20 +987,8 @@ class Painter { continue; } - // For symbol layers in the translucent pass, we add extra tiles to the renderable set - // for cross-tile symbol fading. Symbol layers don't use tile clipping, so no need to render - // separate clipping masks - let coords: ?Array; - - if (sourceCache) { - const coordsSet = layer.type === 'symbol' ? coordsDescendingSymbol : - (layer.is3D() ? coordsSortedByDistance : coordsDescending); - - coords = coordsSet[sourceCache.id]; - } - this._renderTileClippingMasks(layer, sourceCache, sourceCache ? coordsAscending[sourceCache.id] : undefined); - this.renderLayer(this, sourceCache, layer, coords); + this.renderLayer(this, sourceCache, layer, coordsForTranslucentLayer(layer, sourceCache)); // Render ground shadows after the last shadow caster layer if (!terrain && shadowRenderer && shadowLayers > 0 && layer.hasShadowPass() && --shadowLayers === 0) { @@ -934,7 +1018,7 @@ class Painter { } if (this.options.showTileBoundaries || this.options.showQueryGeometry || this.options.showTileAABBs) { - //Use source with highest maxzoom + // Use source with highest maxzoom let selectedSource = null; orderedLayers.forEach((layer) => { const sourceCache = style.getLayerSourceCache(layer); @@ -946,7 +1030,7 @@ class Painter { }); if (selectedSource) { if (this.options.showTileBoundaries) { - draw.debug(this, selectedSource, selectedSource.getVisibleCoordinates()); + draw.debug(this, selectedSource, selectedSource.getVisibleCoordinates(), Color.red, false, this.options.showParseStatus); } Debug.run(() => { @@ -961,6 +1045,10 @@ class Painter { } } + if (this.terrain && this._debugParams.showTerrainProxyTiles) { + draw.debug(this, this.terrain.proxySourceCache, this.terrain.proxyCoords, new Color(1.0, 0.8, 0.1, 1.0), true, this.options.showParseStatus); + } + if (this.options.showPadding) { drawDebugPadding(this); } @@ -971,7 +1059,7 @@ class Painter { this.frameCounter = (this.frameCounter + 1) % Number.MAX_SAFE_INTEGER; if (this.tileLoaded && this.options.speedIndexTiming) { - this.loadTimeStamps.push(window.performance.now()); + this.loadTimeStamps.push(performance.now()); this.saveCanvasCopy(); } @@ -980,20 +1068,25 @@ class Painter { } } - uploadLayer(painter: Painter, layer: StyleLayer, sourceCache?: SourceCache) { + prepareLayer(layer: StyleLayer) { this.gpuTimingStart(layer); - if (!painter.transform.projection.unsupportedLayers || !painter.transform.projection.unsupportedLayers.includes(layer.type) || - (painter.terrain && layer.type === 'custom')) { - if (upload[`${layer.type}Upload`]) { - upload[`${layer.type}Upload`](painter, sourceCache, layer.scope); - } + + const {unsupportedLayers} = this.transform.projection; + const isLayerSupported = unsupportedLayers ? !unsupportedLayers.includes(layer.type) : true; + const isCustomLayerWithTerrain = this.terrain && layer.type === 'custom'; + + if (prepare[layer.type] && (isLayerSupported || isCustomLayerWithTerrain)) { + const sourceCache = this.style.getLayerSourceCache(layer); + prepare[layer.type](layer, sourceCache, this); } + this.gpuTimingEnd(); } renderLayer(painter: Painter, sourceCache?: SourceCache, layer: StyleLayer, coords?: Array) { if (layer.isHidden(this.transform.zoom)) return; - if (layer.type !== 'background' && layer.type !== 'sky' && layer.type !== 'custom' && layer.type !== 'model' && layer.type !== 'raster' && !(coords && coords.length)) return; + if (layer.type !== 'background' && layer.type !== 'sky' && layer.type !== 'custom' && layer.type !== 'model' && layer.type !== 'raster' && layer.type !== 'raster-particle' && !(coords && coords.length)) return; + this.id = layer.id; this.gpuTimingStart(layer); @@ -1158,7 +1251,7 @@ class Painter { return this.style && !!this.style.getTerrain() && !!this.terrain && !this.terrain.renderingToTexture; } - terrainUseFloatDEM(): boolean { + linearFloatFilteringSupported(): boolean { const context = this.context; return context.extTextureFloatLinear != null; } @@ -1198,7 +1291,7 @@ class Painter { } if (this.terrainRenderModeElevated()) { defines.push('TERRAIN'); - if (this.terrainUseFloatDEM()) defines.push('TERRAIN_DEM_FLOAT_FORMAT'); + if (this.linearFloatFilteringSupported()) defines.push('TERRAIN_DEM_FLOAT_FORMAT'); if (zeroExaggeration) defines.push('ZERO_EXAGGERATION'); } if (this.transform.projection.name === 'globe') defines.push('GLOBE'); @@ -1262,7 +1355,7 @@ class Painter { initDebugOverlayCanvas() { if (this.debugOverlayCanvas == null) { - this.debugOverlayCanvas = window.document.createElement('canvas'); + this.debugOverlayCanvas = document.createElement('canvas'); this.debugOverlayCanvas.width = 512; this.debugOverlayCanvas.height = 512; const gl = this.context.gl; diff --git a/src/render/program.js b/src/render/program.js index da9baec6f47..aea5cdc6c7b 100644 --- a/src/render/program.js +++ b/src/render/program.js @@ -31,11 +31,12 @@ import ColorMode from '../gl/color_mode.js'; import type CullFaceMode from '../gl/cull_face_mode.js'; import type {UniformBindings, UniformValues} from './uniform_binding.js'; import type {BinderUniform} from '../data/program_configuration.js'; -import Painter from './painter.js'; +import type Painter from './painter.js'; import type {Segment} from "../data/segment"; import Color from '../style-spec/util/color.js'; export type DrawMode = + | $PropertyType | $PropertyType | $PropertyType | $PropertyType; @@ -49,17 +50,6 @@ type ShaderSource = { fragmentIncludes: Array }; -function getTokenizedAttributes(array: Array): Array { - const result = []; - - for (let i = 0; i < array.length; i++) { - if (array[i] === null) continue; - const token = array[i].split(' '); - result.push(token.pop()); - } - return result; -} - const debugWireframe2DLayerProgramNames = [ 'fill', 'fillOutline', 'fillPattern', 'line', 'linePattern', @@ -114,9 +104,8 @@ class Program { this.name = name; this.fixedDefines = [...fixedDefines]; - const staticAttrInfo = getTokenizedAttributes(source.staticAttributes); const dynamicAttrInfo = configuration ? configuration.getBinderAttributes() : []; - const allAttrInfo = staticAttrInfo.concat(dynamicAttrInfo); + const allAttrInfo = (source.staticAttributes || []).concat(dynamicAttrInfo); let defines = configuration ? configuration.defines() : []; defines = defines.concat(fixedDefines.map((define) => `#define ${define}`)); @@ -399,7 +388,7 @@ class Program { uniformValues: UniformValues, layerID: string, layoutVertexBuffer: VertexBuffer, - indexBuffer: IndexBuffer, + indexBuffer: IndexBuffer | void, segments: SegmentVector, currentProperties: any, zoom: ?number, @@ -427,6 +416,7 @@ class Program { } const primitiveSize = { + [gl.POINTS]: 1, [gl.LINES]: 2, [gl.TRIANGLES]: 3, [gl.LINE_STRIP]: 1 @@ -448,20 +438,23 @@ class Program { ); if (instanceCount && instanceCount > 1) { + assert(indexBuffer); gl.drawElementsInstanced( drawMode, segment.primitiveLength * primitiveSize, gl.UNSIGNED_SHORT, segment.primitiveOffset * primitiveSize * 2, instanceCount); - } else { + } else if (indexBuffer) { gl.drawElements( drawMode, segment.primitiveLength * primitiveSize, gl.UNSIGNED_SHORT, segment.primitiveOffset * primitiveSize * 2); + } else { + gl.drawArrays(drawMode, segment.vertexOffset, segment.vertexLength); } - if (drawMode === gl.TRIANGLES) { + if (drawMode === gl.TRIANGLES && indexBuffer) { // Handle potential wireframe rendering for current draw call this._drawDebugWireframe(painter, depthMode, stencilMode, colorMode, indexBuffer, segment, currentProperties, zoom, configuration, instanceCount); diff --git a/src/render/program/fill_extrusion_program.js b/src/render/program/fill_extrusion_program.js index f7e2e528624..7bafc28b03c 100644 --- a/src/render/program/fill_extrusion_program.js +++ b/src/render/program/fill_extrusion_program.js @@ -36,7 +36,8 @@ export type FillExtrusionUniformsType = {| 'u_flood_light_color': Uniform3f, 'u_vertical_scale': Uniform1f, 'u_flood_light_intensity': Uniform1f, - 'u_ground_shadow_factor': Uniform3f + 'u_ground_shadow_factor': Uniform3f, + 'u_emissive_strength': Uniform1f |}; export type FillExtrusionDepthUniformsType = {| @@ -102,7 +103,8 @@ const fillExtrusionUniforms = (context: Context): FillExtrusionUniformsType => ( 'u_flood_light_color': new Uniform3f(context), 'u_vertical_scale': new Uniform1f(context), 'u_flood_light_intensity': new Uniform1f(context), - 'u_ground_shadow_factor': new Uniform3f(context) + 'u_ground_shadow_factor': new Uniform3f(context), + 'u_emissive_strength': new Uniform1f(context) }); const fillExtrusionDepthUniforms = (context: Context): FillExtrusionDepthUniformsType => ({ @@ -167,7 +169,8 @@ const fillExtrusionUniformValues = ( floodLightColor: [number, number, number], verticalScale: number, floodLightIntensity: number, - groundShadowFactor: [number, number, number] + groundShadowFactor: [number, number, number], + emissiveStrength: number ): UniformValues => { const light = painter.style.light; const _lp = light.properties.get('position'); @@ -200,7 +203,8 @@ const fillExtrusionUniformValues = ( 'u_flood_light_color': floodLightColor, 'u_vertical_scale': verticalScale, 'u_flood_light_intensity': floodLightIntensity, - 'u_ground_shadow_factor': groundShadowFactor + 'u_ground_shadow_factor': groundShadowFactor, + 'u_emissive_strength': emissiveStrength }; if (tr.projection.name === 'globe') { @@ -245,7 +249,7 @@ const fillExtrusionPatternUniformValues = ( ): UniformValues => { const uniformValues = fillExtrusionUniformValues( matrix, painter, shouldUseVerticalGradient, opacity, aoIntensityRadius, edgeRadius, coord, - heightLift, zoomTransition, mercatorCenter, invMatrix, floodLightColor, verticalScale, 1.0, [0, 0, 0]); + heightLift, zoomTransition, mercatorCenter, invMatrix, floodLightColor, verticalScale, 1.0, [0, 0, 0], 0); const heightFactorUniform = { 'u_height_factor': -Math.pow(2, coord.overscaledZ) / tile.tileSize / 8 }; diff --git a/src/render/program/hillshade_program.js b/src/render/program/hillshade_program.js index 68c04cc7015..850ef9701c1 100644 --- a/src/render/program/hillshade_program.js +++ b/src/render/program/hillshade_program.js @@ -7,8 +7,7 @@ import { Uniform1f, Uniform2f, UniformColor, - UniformMatrix4f, - Uniform4f + UniformMatrix4f } from '../uniform_binding.js'; import EXTENT from '../../style-spec/data/extent.js'; import MercatorCoordinate from '../../geo/mercator_coordinate.js'; @@ -37,8 +36,7 @@ export type HillshadePrepareUniformsType = {| 'u_matrix': UniformMatrix4f, 'u_image': Uniform1i, 'u_dimension': Uniform2f, - 'u_zoom': Uniform1f, - 'u_unpack': Uniform4f + 'u_zoom': Uniform1f |}; export type HillshadeDefinesType = 'TERRAIN_DEM_FLOAT_FORMAT'; @@ -58,8 +56,7 @@ const hillshadePrepareUniforms = (context: Context): HillshadePrepareUniformsTyp 'u_matrix': new UniformMatrix4f(context), 'u_image': new Uniform1i(context), 'u_dimension': new Uniform2f(context), - 'u_zoom': new Uniform1f(context), - 'u_unpack': new Uniform4f(context) + 'u_zoom': new Uniform1f(context) }); const hillshadeUniformValues = ( @@ -112,8 +109,7 @@ const hillshadeUniformPrepareValues = ( 'u_matrix': matrix, 'u_image': 1, 'u_dimension': [stride, stride], - 'u_zoom': tileID.overscaledZ, - 'u_unpack': dem.unpackVector + 'u_zoom': tileID.overscaledZ }; }; diff --git a/src/render/program/line_program.js b/src/render/program/line_program.js index 813f46994c7..55517ca2633 100644 --- a/src/render/program/line_program.js +++ b/src/render/program/line_program.js @@ -39,10 +39,11 @@ export type LinePatternUniformsType = {| 'u_units_to_pixels': Uniform2f, 'u_image': Uniform1i, 'u_tile_units_to_pixels': Uniform1f, - 'u_alpha_discard_threshold': Uniform1f + 'u_alpha_discard_threshold': Uniform1f, + 'u_trim_offset': Uniform2f, |}; -export type LineDefinesType = 'RENDER_LINE_GRADIENT' | 'RENDER_LINE_DASH' | 'RENDER_LINE_TRIM_OFFSET' | 'RENDER_LINE_BORDER'; +export type LineDefinesType = 'RENDER_LINE_GRADIENT' | 'RENDER_LINE_DASH' | 'RENDER_LINE_TRIM_OFFSET' | 'RENDER_LINE_BORDER' | 'LINE_JOIN_NONE'; const lineUniforms = (context: Context): LineUniformsType => ({ 'u_matrix': new UniformMatrix4f(context), @@ -67,7 +68,8 @@ const linePatternUniforms = (context: Context): LinePatternUniformsType => ({ 'u_image': new Uniform1i(context), 'u_units_to_pixels': new Uniform2f(context), 'u_tile_units_to_pixels': new Uniform1f(context), - 'u_alpha_discard_threshold': new Uniform1f(context) + 'u_alpha_discard_threshold': new Uniform1f(context), + 'u_trim_offset': new Uniform2f(context), }); const lineUniformValues = ( @@ -105,7 +107,8 @@ const linePatternUniformValues = ( tile: Tile, layer: LineStyleLayer, matrix: ?Float32Array, - pixelRatio: number + pixelRatio: number, + trimOffset: [number, number], ): UniformValues => { const transform = painter.transform; return { @@ -120,7 +123,8 @@ const linePatternUniformValues = ( 1 / transform.pixelsToGLUnits[0], 1 / transform.pixelsToGLUnits[1] ], - 'u_alpha_discard_threshold': 0.0 + 'u_alpha_discard_threshold': 0.0, + 'u_trim_offset': trimOffset }; }; @@ -149,6 +153,13 @@ const lineDefinesValues = (layer: LineStyleLayer): LineDefinesType[] => { const hasBorder = layer.paint.get('line-border-width').constantOr(1.0) !== 0.0; if (hasBorder) values.push('RENDER_LINE_BORDER'); + + const hasJoinNone = layer.layout.get('line-join').constantOr('miter') === 'none'; + const hasPattern = !!layer.paint.get('line-pattern').constantOr((1: any)); + if (hasJoinNone && hasPattern) { + values.push('LINE_JOIN_NONE'); + } + return values; }; diff --git a/src/render/program/program_uniforms.js b/src/render/program/program_uniforms.js index b0e8978b3e2..43f25133bb7 100644 --- a/src/render/program/program_uniforms.js +++ b/src/render/program/program_uniforms.js @@ -2,6 +2,7 @@ import type {CircleDefinesType} from './circle_program.js'; import type {RasterDefinesType} from './raster_program.js'; +import type {RasterParticleDefinesType} from './raster_particle_program.js'; import type {SymbolDefinesType} from './symbol_program.js'; import type {LineDefinesType} from './line_program.js'; import type {HillshadeDefinesType} from "./hillshade_program.js"; @@ -15,6 +16,7 @@ import {heatmapUniforms, heatmapTextureUniforms} from './heatmap_program.js'; import {hillshadeUniforms, hillshadePrepareUniforms} from './hillshade_program.js'; import {lineUniforms, linePatternUniforms} from './line_program.js'; import {rasterUniforms} from './raster_program.js'; +import {rasterParticleUniforms, rasterParticleTextureUniforms, rasterParticleDrawUniforms, rasterParticleUpdateUniforms} from './raster_particle_program.js'; import {symbolIconUniforms, symbolSDFUniforms, symbolTextAndIconUniforms} from './symbol_program.js'; import {backgroundUniforms, backgroundPatternUniforms} from './background_program.js'; import {terrainRasterUniforms} from '../../terrain/terrain_raster_program.js'; @@ -30,7 +32,7 @@ import {groundShadowUniforms} from '../../../3d-style/render/program/ground_shad import {starsUniforms} from '../../terrain/stars_program.js'; export type FogDefinesType = ['FOG', 'FOG_DITHERING']; -export type DynamicDefinesType = CircleDefinesType | SymbolDefinesType | LineDefinesType | HeatmapDefinesType | DebugDefinesType | GlobeDefinesType | RasterDefinesType | FogDefinesType | HillshadeDefinesType; +export type DynamicDefinesType = CircleDefinesType | SymbolDefinesType | LineDefinesType | HeatmapDefinesType | DebugDefinesType | GlobeDefinesType | RasterDefinesType | RasterParticleDefinesType | FogDefinesType | HillshadeDefinesType; export const programUniforms = { fillExtrusion: fillExtrusionUniforms, @@ -53,6 +55,10 @@ export const programUniforms = { line: lineUniforms, linePattern: linePatternUniforms, raster: rasterUniforms, + rasterParticle: rasterParticleUniforms, + rasterParticleTexture: rasterParticleTextureUniforms, + rasterParticleDraw: rasterParticleDrawUniforms, + rasterParticleUpdate: rasterParticleUpdateUniforms, symbolIcon: symbolIconUniforms, symbolSDF: symbolSDFUniforms, symbolTextAndIcon: symbolTextAndIconUniforms, diff --git a/src/render/program/raster_particle_program.js b/src/render/program/raster_particle_program.js new file mode 100644 index 00000000000..6ffeddbdf03 --- /dev/null +++ b/src/render/program/raster_particle_program.js @@ -0,0 +1,196 @@ +// @flow + +import { + Uniform1i, + Uniform1f, + Uniform2f, + Uniform4f, + UniformMatrix3f, + UniformMatrix4f +} from '../uniform_binding.js'; +import {PARTICLE_POS_SCALE, PARTICLE_POS_OFFSET} from '../raster_particle_state.js'; + +import type Context from '../../gl/context.js'; +import type {UniformValues} from '../uniform_binding.js'; + +export type RasterParticleUniformsType = {| + 'u_matrix': UniformMatrix4f, + 'u_normalize_matrix': UniformMatrix4f, + 'u_globe_matrix': UniformMatrix4f, + 'u_merc_matrix': UniformMatrix4f, + 'u_grid_matrix': UniformMatrix3f, + 'u_tl_parent': Uniform2f, + 'u_scale_parent': Uniform1f, + 'u_fade_t': Uniform1f, + 'u_opacity': Uniform1f, + 'u_image0': Uniform1i, + 'u_image1': Uniform1i, + 'u_raster_elevation': Uniform1f, + 'u_zoom_transition': Uniform1f, + 'u_merc_center': Uniform2f, + 'u_cutoff_params': Uniform4f +|}; + +export type RasterParticleDefinesType = 'RASTER_ARRAY' | 'RENDER_CUTOFF' | 'DATA_FORMAT_UINT32' | 'DATA_FORMAT_UINT16' | 'DATA_FORMAT_UINT8'; + +const rasterParticleUniforms = (context: Context): RasterParticleUniformsType => ({ + 'u_matrix': new UniformMatrix4f(context), + 'u_normalize_matrix': new UniformMatrix4f(context), + 'u_globe_matrix': new UniformMatrix4f(context), + 'u_merc_matrix': new UniformMatrix4f(context), + 'u_grid_matrix': new UniformMatrix3f(context), + 'u_tl_parent': new Uniform2f(context), + 'u_scale_parent': new Uniform1f(context), + 'u_fade_t': new Uniform1f(context), + 'u_opacity': new Uniform1f(context), + 'u_image0': new Uniform1i(context), + 'u_image1': new Uniform1i(context), + 'u_raster_elevation': new Uniform1f(context), + 'u_zoom_transition': new Uniform1f(context), + 'u_merc_center': new Uniform2f(context), + 'u_cutoff_params': new Uniform4f(context) +}); + +const rasterParticleUniformValues = ( + matrix: Float32Array, + normalizeMatrix: Float32Array, + globeMatrix: Float32Array, + mercMatrix: Float32Array, + gridMatrix: Float32Array, + parentTL: [number, number], + zoomTransition: number, + mercatorCenter: [number, number], + cutoffParams: [number, number, number, number], + parentScaleBy: number, + fade: {mix: number, opacity: number}, + elevation: number +): UniformValues => ({ + 'u_matrix': matrix, + 'u_normalize_matrix': normalizeMatrix, + 'u_globe_matrix': globeMatrix, + 'u_merc_matrix': mercMatrix, + 'u_grid_matrix': gridMatrix, + 'u_tl_parent': parentTL, + 'u_scale_parent': parentScaleBy, + 'u_fade_t': fade.mix, + 'u_opacity': fade.opacity, + 'u_image0': 0, + 'u_image1': 1, + 'u_raster_elevation': elevation, + 'u_zoom_transition': zoomTransition, + 'u_merc_center': mercatorCenter, + 'u_cutoff_params': cutoffParams +}); + +export type RasterParticleTextureUniforms = {| + 'u_texture': Uniform1i, + 'u_opacity': Uniform1f +|}; + +const rasterParticleTextureUniforms = (context: Context): RasterParticleTextureUniforms => ({ + 'u_texture': new Uniform1i(context), + 'u_opacity': new Uniform1f(context) +}); + +const rasterParticleTextureUniformValues = ( + textureUnit: number, + opacity: number +): UniformValues => ({ + 'u_texture': textureUnit, + 'u_opacity': opacity +}); + +export type RasterParticleDrawUniformsType = {| + 'u_particle_texture': Uniform1i, + 'u_particle_texture_side_len': Uniform1f, + 'u_tile_offset': Uniform2f, + 'u_velocity': Uniform1i, + 'u_color_ramp': Uniform1i, + 'u_velocity_res': Uniform2f, + 'u_max_speed': Uniform1f, + 'u_uv_offset': Uniform2f, + 'u_data_scale': Uniform4f, + 'u_data_offset': Uniform1f, + 'u_particle_pos_scale': Uniform1f, + 'u_particle_pos_offset': Uniform2f +|}; + +const rasterParticleDrawUniforms = (context: Context): RasterParticleDrawUniformsType => ({ + 'u_particle_texture': new Uniform1i(context), + 'u_particle_texture_side_len': new Uniform1f(context), + 'u_tile_offset': new Uniform2f(context), + 'u_velocity': new Uniform1i(context), + 'u_color_ramp': new Uniform1i(context), + 'u_velocity_res': new Uniform2f(context), + 'u_max_speed': new Uniform1f(context), + 'u_uv_offset': new Uniform2f(context), + 'u_data_scale': new Uniform4f(context), + 'u_data_offset': new Uniform1f(context), + 'u_particle_pos_scale': new Uniform1f(context), + 'u_particle_pos_offset': new Uniform2f(context) +}); + +const rasterParticleDrawUniformValues = (particleTextureUnit: number, particleTextureSideLen: number, tileOffset: [number, number], velocityTextureUnit: number, velocityTextureSize: [number, number], colorRampUnit: number, maxSpeed: number, textureOffset: [number, number], dataScale: [number, number, number, number], dataOffset: number): UniformValues => ({ + 'u_particle_texture': particleTextureUnit, + 'u_particle_texture_side_len': particleTextureSideLen, + 'u_tile_offset': tileOffset, + 'u_velocity': velocityTextureUnit, + 'u_color_ramp': colorRampUnit, + 'u_velocity_res': velocityTextureSize, + 'u_max_speed': maxSpeed, + 'u_uv_offset': textureOffset, + 'u_data_scale': dataScale, + 'u_data_offset': dataOffset, + 'u_particle_pos_scale': PARTICLE_POS_SCALE, + 'u_particle_pos_offset': [PARTICLE_POS_OFFSET, PARTICLE_POS_OFFSET] +}); + +export type RasterParticleUpdateUniformsType = {| + 'u_particle_texture': Uniform1i, + 'u_particle_texture_side_len': Uniform1f, + 'u_velocity': Uniform1i, + 'u_velocity_res': Uniform2f, + 'u_max_speed': Uniform1f, + 'u_speed_factor': Uniform1f, + 'u_reset_rate': Uniform1f, + 'u_rand_seed': Uniform1f, + 'u_uv_offset': Uniform2f, + 'u_data_scale': Uniform4f, + 'u_data_offset': Uniform1f, + 'u_particle_pos_scale': Uniform1f, + 'u_particle_pos_offset': Uniform2f +|}; + +const rasterParticleUpdateUniforms = (context: Context): RasterParticleUpdateUniformsType => ({ + 'u_particle_texture': new Uniform1i(context), + 'u_particle_texture_side_len': new Uniform1f(context), + 'u_velocity': new Uniform1i(context), + 'u_velocity_res': new Uniform2f(context), + 'u_max_speed': new Uniform1f(context), + 'u_speed_factor': new Uniform1f(context), + 'u_reset_rate': new Uniform1f(context), + 'u_rand_seed': new Uniform1f(context), + 'u_uv_offset': new Uniform2f(context), + 'u_data_scale': new Uniform4f(context), + 'u_data_offset': new Uniform1f(context), + 'u_particle_pos_scale': new Uniform1f(context), + 'u_particle_pos_offset': new Uniform2f(context) +}); + +const rasterParticleUpdateUniformValues = (particleTextureUnit: number, particleTextureSideLen: number, velocityTextureUnit: number, velocityTextureSize: [number, number], maxSpeed: number, speedFactor: number, resetRate: number, textureOffset: [number, number], dataScale: [number, number, number, number], dataOffset: number): UniformValues => ({ + 'u_particle_texture': particleTextureUnit, + 'u_particle_texture_side_len': particleTextureSideLen, + 'u_velocity': velocityTextureUnit, + 'u_velocity_res': velocityTextureSize, + 'u_max_speed': maxSpeed, + 'u_speed_factor': speedFactor, + 'u_reset_rate': resetRate, + 'u_rand_seed': Math.random(), + 'u_uv_offset': textureOffset, + 'u_data_scale': dataScale, + 'u_data_offset': dataOffset, + 'u_particle_pos_scale': PARTICLE_POS_SCALE, + 'u_particle_pos_offset': [PARTICLE_POS_OFFSET, PARTICLE_POS_OFFSET] +}); + +export {rasterParticleUniforms, rasterParticleUniformValues, rasterParticleTextureUniforms, rasterParticleTextureUniformValues, rasterParticleDrawUniforms, rasterParticleDrawUniformValues, rasterParticleUpdateUniforms, rasterParticleUpdateUniformValues}; diff --git a/src/render/program/raster_program.js b/src/render/program/raster_program.js index 3157ab0e367..c466aee7199 100644 --- a/src/render/program/raster_program.js +++ b/src/render/program/raster_program.js @@ -6,10 +6,13 @@ import { Uniform2f, Uniform3f, Uniform4f, + UniformMatrix3f, UniformMatrix4f } from '../uniform_binding.js'; +import {computeRasterColorMix, computeRasterColorOffset} from '../raster.js'; import {COLOR_RAMP_RES} from '../../style/style_layer/raster_style_layer.js'; +import {contrastFactor, saturationFactor} from '../../util/util.js'; import type Context from '../../gl/context.js'; import type {UniformValues} from '../uniform_binding.js'; @@ -19,6 +22,8 @@ export type RasterUniformsType = {| 'u_matrix': UniformMatrix4f, 'u_normalize_matrix': UniformMatrix4f, 'u_globe_matrix': UniformMatrix4f, + 'u_merc_matrix': UniformMatrix4f, + 'u_grid_matrix': UniformMatrix3f, 'u_tl_parent': Uniform2f, 'u_scale_parent': Uniform1f, 'u_fade_t': Uniform1f, @@ -31,17 +36,26 @@ export type RasterUniformsType = {| 'u_contrast_factor': Uniform1f, 'u_spin_weights': Uniform3f, 'u_perspective_transform': Uniform2f, + 'u_raster_elevation': Uniform1f, + 'u_zoom_transition': Uniform1f, + 'u_merc_center': Uniform2f, + 'u_cutoff_params': Uniform4f, 'u_colorization_mix': Uniform4f, - 'u_colorization_scale': Uniform2f, + 'u_colorization_offset': Uniform1f, 'u_color_ramp': Uniform1i, + 'u_texture_offset': Uniform2f, + 'u_texture_res': Uniform2f, + 'u_emissive_strength': Uniform1f |}; -export type RasterDefinesType = 'RASTER_COLOR'; +export type RasterDefinesType = 'RASTER_COLOR' | 'RENDER_CUTOFF' | 'RASTER_ARRAY' | 'RASTER_ARRAY_LINEAR'; const rasterUniforms = (context: Context): RasterUniformsType => ({ 'u_matrix': new UniformMatrix4f(context), 'u_normalize_matrix': new UniformMatrix4f(context), 'u_globe_matrix': new UniformMatrix4f(context), + 'u_merc_matrix': new UniformMatrix4f(context), + 'u_grid_matrix': new UniformMatrix3f(context), 'u_tl_parent': new Uniform2f(context), 'u_scale_parent': new Uniform1f(context), 'u_fade_t': new Uniform1f(context), @@ -54,27 +68,46 @@ const rasterUniforms = (context: Context): RasterUniformsType => ({ 'u_contrast_factor': new Uniform1f(context), 'u_spin_weights': new Uniform3f(context), 'u_perspective_transform': new Uniform2f(context), + 'u_raster_elevation': new Uniform1f(context), + 'u_zoom_transition': new Uniform1f(context), + 'u_merc_center': new Uniform2f(context), + 'u_cutoff_params': new Uniform4f(context), 'u_colorization_mix': new Uniform4f(context), - 'u_colorization_scale': new Uniform2f(context), + 'u_colorization_offset': new Uniform1f(context), 'u_color_ramp': new Uniform1i(context), + 'u_texture_offset': new Uniform2f(context), + 'u_texture_res': new Uniform2f(context), + 'u_emissive_strength': new Uniform1f(context) }); const rasterUniformValues = ( matrix: Float32Array, normalizeMatrix: Float32Array, globeMatrix: Float32Array, + mercMatrix: Float32Array, + gridMatrix: Float32Array, parentTL: [number, number], + zoomTransition: number, + mercatorCenter: [number, number], + cutoffParams: [number, number, number, number], parentScaleBy: number, fade: {mix: number, opacity: number}, layer: RasterStyleLayer, perspectiveTransform: [number, number], + elevation: number, colorRampUnit: number, - colorMapInputMix: [number, number, number, number], - colorRange: [number, number] + colorMix: [number, number, number, number], + colorOffset: number, + colorRange: [number, number], + tileSize: number, + buffer: number, + emissiveStrength: number ): UniformValues => ({ 'u_matrix': matrix, 'u_normalize_matrix': normalizeMatrix, 'u_globe_matrix': globeMatrix, + 'u_merc_matrix': mercMatrix, + 'u_grid_matrix': gridMatrix, 'u_tl_parent': parentTL, 'u_scale_parent': parentScaleBy, 'u_fade_t': fade.mix, @@ -87,11 +120,59 @@ const rasterUniformValues = ( 'u_contrast_factor': contrastFactor(layer.paint.get('raster-contrast')), 'u_spin_weights': spinWeights(layer.paint.get('raster-hue-rotate')), 'u_perspective_transform': perspectiveTransform, - 'u_colorization_mix': colorMapInputMix, - 'u_colorization_scale': colorScaleFactors(colorRange), + 'u_raster_elevation': elevation, + 'u_zoom_transition': zoomTransition, + 'u_merc_center': mercatorCenter, + 'u_cutoff_params': cutoffParams, + 'u_colorization_mix': computeRasterColorMix(COLOR_RAMP_RES, colorMix, colorRange), + 'u_colorization_offset': computeRasterColorOffset(COLOR_RAMP_RES, colorOffset, colorRange), 'u_color_ramp': colorRampUnit, + 'u_texture_offset': [ + buffer / (tileSize + 2 * buffer), + tileSize / (tileSize + 2 * buffer) + ], + 'u_texture_res': [tileSize + 2 * buffer, tileSize + 2 * buffer], + 'u_emissive_strength': emissiveStrength }); +const rasterPoleUniformValues = ( + matrix: Float32Array, + normalizeMatrix: Float32Array, + globeMatrix: Float32Array, + zoomTransition: number, + fade: {mix: number, opacity: number}, + layer: RasterStyleLayer, + perspectiveTransform: [number, number], + elevation: number, + colorRampUnit: number, + colorMix: [number, number, number, number], + colorOffset: number, + colorRange: [number, number], + emissiveStrength: number +): UniformValues => (rasterUniformValues( + matrix, + normalizeMatrix, + globeMatrix, + new Float32Array(16), + new Float32Array(9), + [0, 0], + zoomTransition, + [0, 0], + [0, 0, 0, 0], + 1, + fade, + layer, + perspectiveTransform || [0, 0], + elevation, + colorRampUnit, + colorMix, + colorOffset, + colorRange, + 1, + 0, + emissiveStrength +)); + function spinWeights(angle: number) { angle *= Math.PI / 180; const s = Math.sin(angle); @@ -103,32 +184,4 @@ function spinWeights(angle: number) { ]; } -function contrastFactor(contrast: number) { - return contrast > 0 ? - 1 / (1 - contrast) : - 1 + contrast; -} - -function saturationFactor(saturation: number) { - return saturation > 0 ? - 1 - 1 / (1.001 - saturation) : - -saturation; -} - -function colorScaleFactors ([min, max]: [number, number]) { - if (min === max) return [min, max]; - - // Account for tabulated color scale values which are defined at texel *centers*. Without this, - // there is up to a half-texel misalignment between the intended value and the evaluated color. - // This makes it much more straightforward to use precisely tabulated, categorical colors. - const center = (max + min) / 2; - const delta = (max - min) / 2 * COLOR_RAMP_RES / (COLOR_RAMP_RES - 1); - min = center - delta; - max = center + delta; - - // Precompute the offset and delta so that operations are moved out of the shader and - // the raster value may be computed in-shader as `colorScale[0] + colorScale[1] * inputValue` - return [-min / (max - min), 1 / (max - min)]; -} - -export {rasterUniforms, rasterUniformValues}; +export {rasterUniforms, rasterUniformValues, rasterPoleUniformValues}; diff --git a/src/render/program/symbol_program.js b/src/render/program/symbol_program.js index 420e1131f54..71507ae0048 100644 --- a/src/render/program/symbol_program.js +++ b/src/render/program/symbol_program.js @@ -43,7 +43,8 @@ export type SymbolIconUniformsType = {| 'u_up_vector': Uniform3f, 'u_ecef_origin': Uniform3f, 'u_texture': Uniform1i, - 'u_icon_transition': Uniform1f + 'u_icon_transition': Uniform1f, + 'u_color_adj_mat': UniformMatrix4f, |}; export type SymbolSDFUniformsType = {| @@ -124,7 +125,8 @@ const symbolIconUniforms = (context: Context): SymbolIconUniformsType => ({ 'u_up_vector': new Uniform3f(context), 'u_ecef_origin': new Uniform3f(context), 'u_texture': new Uniform1i(context), - 'u_icon_transition': new Uniform1f(context) + 'u_icon_transition': new Uniform1f(context), + 'u_color_adj_mat': new UniformMatrix4f(context) }); const symbolSDFUniforms = (context: Context): SymbolSDFUniformsType => ({ @@ -198,7 +200,8 @@ const symbolIconUniformValues = ( invMatrix: Float32Array, upVector: [number, number, number], projection: Projection, - transition: ?number + colorAdjustmentMatrix: ?Float32Array, + transition: ?number, ): UniformValues => { const transform = painter.transform; @@ -226,6 +229,7 @@ const symbolIconUniformValues = ( 'u_ecef_origin': [0, 0, 0], 'u_tile_matrix': identityMatrix, 'u_up_vector': [0, -1, 0], + 'u_color_adj_mat': colorAdjustmentMatrix, 'u_icon_transition': transition ? transition : 0.0 }; @@ -240,6 +244,7 @@ const symbolIconUniformValues = ( values['u_up_vector'] = upVector; } + /* $FlowFixMe[incompatible-return] */ return values; }; diff --git a/src/render/raster.js b/src/render/raster.js new file mode 100644 index 00000000000..5caac8542c2 --- /dev/null +++ b/src/render/raster.js @@ -0,0 +1,38 @@ +// @flow + +function computeRasterColorMix(colorRampRes: number, [mixR, mixG, mixB, mixA]: [number, number, number, number], [min, max]: [number, number]): [number, number, number, number] { + if (min === max) return [0, 0, 0, 0]; + + // Together with the `offset`, the mix vector transforms the encoded integer + // input into a numeric value. To minimize work, we modify this vector to + // perform extra steps on the CPU, before rendering. + // + // To a first cut, we map `min` to the texture coordinate 0, and `max` to texture + // coordinate 1. However, this would align the samples with the *edges* of + // tabulated texels rather than the centers. This makes it difficult to precisely + // position values relative to the tabulated colors. + // + // Therefore given color map resolution N, we actually map `min` to 1 / 2N and + // `max` to 1 - 1 / 2N. When you work out a few lines of algebra, the scale factor + // below is the result. + // + // Similarly, computerRasterColorOffset contains the counterpart of this equation + // by which the constant offset is adjusted. + const factor = 255 * (colorRampRes - 1) / (colorRampRes * (max - min)); + + return [ + mixR * factor, + mixG * factor, + mixB * factor, + mixA * factor + ]; +} + +function computeRasterColorOffset(colorRampRes: number, offset: number, [min, max]: [number, number]): number { + if (min === max) return 0; + + // See above for an explanation. + return 0.5 / colorRampRes + (offset - min) * (colorRampRes - 1) / (colorRampRes * (max - min)); +} + +export {computeRasterColorMix, computeRasterColorOffset}; diff --git a/src/render/raster_particle_state.js b/src/render/raster_particle_state.js new file mode 100644 index 00000000000..3aceb794642 --- /dev/null +++ b/src/render/raster_particle_state.js @@ -0,0 +1,112 @@ +// @flow + +import browser from '../util/browser.js'; +import Context from '../gl/context.js'; +import {mulberry32} from '../style-spec/util/random.js'; +import {ParticleIndexLayoutArray} from '../data/array_types.js'; +import particleAttributes from '../data/particle_attributes.js'; +import {RGBAImage} from '../util/image.js'; +import SegmentVector from '../data/segment.js'; +import Texture from './texture.js'; +import assert from 'assert'; + +import type {OverscaledTileID} from "../source/tile_id"; +import type {TextureImage} from './texture.js'; +import type VertexBuffer from '../gl/vertex_buffer.js'; + +export const PARTICLE_POS_SCALE = 1.3; +export const PARTICLE_POS_OFFSET = 0.5 * (PARTICLE_POS_SCALE - 1.0); + +class RasterParticleState { + context: Context; + particleTexture0: Texture; + particleTexture1: Texture; + particleIndexBuffer: VertexBuffer; + particleSegment: SegmentVector; + targetColorTexture: Texture; + backgroundColorTexture: Texture; + particleTextureDimension: number; + lastInvalidatedAt: number; + + constructor(context: Context, id: OverscaledTileID, textureSize: [number, number], particleTextureDimension: number): void { + const emptyImage: TextureImage = { + width: textureSize[0], + height: textureSize[1], + data: null + }; + const gl = context.gl; + this.targetColorTexture = new Texture(context, emptyImage, gl.RGBA, {useMipmap: false}); + this.backgroundColorTexture = new Texture(context, emptyImage, gl.RGBA, {useMipmap: false}); + this.context = context; + + this.setParticleTextureDimension(id, particleTextureDimension); + this.lastInvalidatedAt = 0; + } + + setParticleTextureDimension(id: OverscaledTileID, particleTextureDimension: number) { + if (this.particleTextureDimension === particleTextureDimension) { + return; + } + + if (this.particleTexture0 || this.particleTexture1 || this.particleIndexBuffer || this.particleSegment) { + assert(this.particleTexture0 && this.particleTexture1 && this.particleIndexBuffer && this.particleSegment); + this.particleTexture0.destroy(); + this.particleTexture1.destroy(); + this.particleIndexBuffer.destroy(); + this.particleSegment.destroy(); + } + + const gl = this.context.gl; + + const numParticles = particleTextureDimension * particleTextureDimension; + const particlePositions = new Uint8Array(numParticles * 4); + + const invScale = 1.0 / PARTICLE_POS_SCALE; + const srand = mulberry32(id.key); + // Encode random particle positions into RGBA pixels + for (let i = 0; i < particlePositions.length; i += 4) { + // Decoded positions in shader: (PARTICLE_POS_SCALE * p - PARTICLE_POS_OFFSET), where p ∈ [0, 1]. + // x, y are the inverse of the decoded position. + const x = invScale * (srand() + PARTICLE_POS_OFFSET); + const y = invScale * (srand() + PARTICLE_POS_OFFSET); + // Encode fractional part into RG, integral part into BA. + particlePositions[i + 0] = Math.floor(256 * ((255 * x) % 1)); + particlePositions[i + 1] = Math.floor(256 * ((255 * y) % 1)); + particlePositions[i + 2] = Math.floor(256 * x); + particlePositions[i + 3] = Math.floor(256 * y); + } + const particleImage = new RGBAImage({width: particleTextureDimension, height: particleTextureDimension}, particlePositions); + this.particleTexture0 = new Texture(this.context, particleImage, gl.RGBA, {premultiply: false, useMipmap: false}); + this.particleTexture1 = new Texture(this.context, particleImage, gl.RGBA, {premultiply: false, useMipmap: false}); + + const particleIndices = new ParticleIndexLayoutArray(); + particleIndices.reserve(numParticles); + for (let i = 0; i < numParticles; i++) { + particleIndices.emplaceBack(i); + } + this.particleIndexBuffer = this.context.createVertexBuffer(particleIndices, particleAttributes.members, true); + + this.particleSegment = SegmentVector.simpleSegment(0, 0, this.particleIndexBuffer.length, 0); + this.particleTextureDimension = particleTextureDimension; + } + + update(layerLastInvalidatedAt: number): boolean { + if (this.lastInvalidatedAt < layerLastInvalidatedAt) { + this.lastInvalidatedAt = browser.now(); + return false; + } + + return true; + } + + destroy() { + this.targetColorTexture.destroy(); + this.backgroundColorTexture.destroy(); + this.particleIndexBuffer.destroy(); + this.particleTexture0.destroy(); + this.particleTexture1.destroy(); + this.particleSegment.destroy(); + } +} + +export default RasterParticleState; diff --git a/src/render/texture.js b/src/render/texture.js index 49c310f2305..3dd736451eb 100644 --- a/src/render/texture.js +++ b/src/render/texture.js @@ -1,7 +1,5 @@ // @flow -import window from '../util/window.js'; - import type Context from '../gl/context.js'; import type {RGBAImage, AlphaImage} from '../util/image.js'; import {Float32Image} from '../util/image.js'; @@ -9,8 +7,14 @@ import assert from 'assert'; export type TextureFormat = | $PropertyType - | $PropertyType - | $PropertyType; + | $PropertyType + | $PropertyType + | $PropertyType + | $PropertyType; +export type TextureType = + | $PropertyType + | $PropertyType + | $PropertyType; export type TextureFilter = | $PropertyType | $PropertyType @@ -62,7 +66,6 @@ class Texture { const {width, height} = image; const {context} = this; const {gl} = context; - const {HTMLImageElement, HTMLCanvasElement, HTMLVideoElement, ImageData, ImageBitmap} = window; gl.bindTexture(gl.TEXTURE_2D, this.texture); @@ -75,24 +78,23 @@ class Texture { if (image instanceof HTMLImageElement || image instanceof HTMLCanvasElement || image instanceof HTMLVideoElement || image instanceof ImageData || (ImageBitmap && image instanceof ImageBitmap)) { let baseFormat = this.format; - // $FlowFixMe[prop-missing] - Flow cannot check for gl.R8 if (this.format === gl.R8) { - // $FlowFixMe[prop-missing] - Flow cannot check for gl.RED baseFormat = gl.RED; } gl.texImage2D(gl.TEXTURE_2D, 0, this.format, baseFormat, gl.UNSIGNED_BYTE, image); } else { let internalFormat = this.format; let format = this.format; - let type = gl.UNSIGNED_BYTE; + let type: TextureType = gl.UNSIGNED_BYTE; if (this.format === gl.DEPTH_COMPONENT) { // $FlowFixMe[incompatible-type] internalFormat = gl.DEPTH_COMPONENT16; - // $FlowFixMe[incompatible-type] type = gl.UNSIGNED_SHORT; } - + if (this.format === gl.R8) { + format = gl.RED; + } if (this.format === gl.R32F) { assert(image instanceof Float32Image); type = gl.FLOAT; @@ -107,7 +109,7 @@ class Texture { gl.texSubImage2D(gl.TEXTURE_2D, 0, x, y, gl.RGBA, gl.UNSIGNED_BYTE, image); } else { let format = this.format; - let type = gl.UNSIGNED_BYTE; + let type: TextureType = gl.UNSIGNED_BYTE; if (this.format === gl.R32F) { assert(image instanceof Float32Image); @@ -126,7 +128,7 @@ class Texture { } } - bind(filter: TextureFilter, wrap: TextureWrap) { + bind(filter: TextureFilter, wrap: TextureWrap, ignoreMipMap: boolean = false) { const {context} = this; const {gl} = context; gl.bindTexture(gl.TEXTURE_2D, this.texture); @@ -134,7 +136,7 @@ class Texture { if (filter !== this.minFilter) { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filter); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, - this.useMipmap ? (filter === gl.NEAREST ? gl.NEAREST_MIPMAP_NEAREST : gl.LINEAR_MIPMAP_NEAREST) : filter + (this.useMipmap && !ignoreMipMap) ? (filter === gl.NEAREST ? gl.NEAREST_MIPMAP_NEAREST : gl.LINEAR_MIPMAP_LINEAR) : filter ); this.minFilter = filter; } @@ -157,7 +159,7 @@ class Texture { } if (minFilter !== this.minFilter) { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, - this.useMipmap ? (minFilter === gl.NEAREST ? gl.NEAREST_MIPMAP_NEAREST : gl.LINEAR_MIPMAP_NEAREST) : minFilter + this.useMipmap ? (minFilter === gl.NEAREST ? gl.NEAREST_MIPMAP_NEAREST : gl.LINEAR_MIPMAP_LINEAR) : minFilter ); this.minFilter = minFilter; } diff --git a/src/render/uniform_binding.js b/src/render/uniform_binding.js index 111c9903722..ea5e51ebcec 100644 --- a/src/render/uniform_binding.js +++ b/src/render/uniform_binding.js @@ -2,11 +2,21 @@ import Color from '../style-spec/util/color.js'; import type Context from '../gl/context.js'; +import type {ObjMap} from '../types/obj-map.js'; export type UniformValues - = $Exact<$ObjMap(u: Uniform) => V>>; + = $Exact(u: IUniform) => V>>; -class Uniform { +export interface IUniform { + gl: WebGL2RenderingContext; + location: WebGLUniformLocation | null | void; + current: T; + initialized: boolean; + fetchUniformLocation(program: WebGLProgram, name: string): boolean; + set(program: WebGLProgram, name: string, v: T): void; +} + +class Uniform implements IUniform { gl: WebGL2RenderingContext; location: ?WebGLUniformLocation; current: T; @@ -25,16 +35,17 @@ class Uniform { return !!this.location; } - +set: (program: WebGLProgram, name: string, v: T) => void; + set(_program: WebGLProgram, _name: string, _v: T): void { + throw new Error('Uniform#set() must be implemented by each concrete Uniform'); + } } -class Uniform1i extends Uniform { +class Uniform1i extends Uniform implements IUniform { constructor(context: Context) { super(context); this.current = 0; } - // $FlowFixMe[method-unbinding] set(program: WebGLProgram, name: string, v: number): void { if (!this.fetchUniformLocation(program, name)) return; if (this.current !== v) { @@ -44,13 +55,12 @@ class Uniform1i extends Uniform { } } -class Uniform1f extends Uniform { +class Uniform1f extends Uniform implements IUniform { constructor(context: Context) { super(context); this.current = 0; } - // $FlowFixMe[method-unbinding] set(program: WebGLProgram, name: string, v: number): void { if (!this.fetchUniformLocation(program, name)) return; if (this.current !== v) { @@ -60,13 +70,12 @@ class Uniform1f extends Uniform { } } -class Uniform2f extends Uniform<[number, number]> { +class Uniform2f extends Uniform<[number, number]> implements IUniform<[number, number]> { constructor(context: Context) { super(context); this.current = [0, 0]; } - // $FlowFixMe[method-unbinding] set(program: WebGLProgram, name: string, v: [number, number]): void { if (!this.fetchUniformLocation(program, name)) return; if (v[0] !== this.current[0] || v[1] !== this.current[1]) { @@ -76,13 +85,12 @@ class Uniform2f extends Uniform<[number, number]> { } } -class Uniform3f extends Uniform<[number, number, number]> { +class Uniform3f extends Uniform<[number, number, number]> implements IUniform<[number, number, number]> { constructor(context: Context) { super(context); this.current = [0, 0, 0]; } - // $FlowFixMe[method-unbinding] set(program: WebGLProgram, name: string, v: [number, number, number]): void { if (!this.fetchUniformLocation(program, name)) return; if (v[0] !== this.current[0] || v[1] !== this.current[1] || v[2] !== this.current[2]) { @@ -92,13 +100,12 @@ class Uniform3f extends Uniform<[number, number, number]> { } } -class Uniform4f extends Uniform<[number, number, number, number]> { +class Uniform4f extends Uniform<[number, number, number, number]> implements IUniform<[number, number, number, number]> { constructor(context: Context) { super(context); this.current = [0, 0, 0, 0]; } - // $FlowFixMe[method-unbinding] set(program: WebGLProgram, name: string, v: [number, number, number, number]): void { if (!this.fetchUniformLocation(program, name)) return; if (v[0] !== this.current[0] || v[1] !== this.current[1] || @@ -109,13 +116,12 @@ class Uniform4f extends Uniform<[number, number, number, number]> { } } -class UniformColor extends Uniform { +class UniformColor extends Uniform implements IUniform { constructor(context: Context) { super(context); this.current = Color.transparent; } - // $FlowFixMe[method-unbinding] set(program: WebGLProgram, name: string, v: Color): void { if (!this.fetchUniformLocation(program, name)) return; if (v.r !== this.current.r || v.g !== this.current.g || @@ -127,13 +133,12 @@ class UniformColor extends Uniform { } const emptyMat4 = new Float32Array(16); -class UniformMatrix4f extends Uniform { +class UniformMatrix4f extends Uniform implements IUniform { constructor(context: Context) { super(context); this.current = emptyMat4; } - // $FlowFixMe[method-unbinding] set(program: WebGLProgram, name: string, v: Float32Array): void { if (!this.fetchUniformLocation(program, name)) return; // The vast majority of matrix comparisons that will trip this set @@ -155,13 +160,12 @@ class UniformMatrix4f extends Uniform { } const emptyMat3 = new Float32Array(9); -class UniformMatrix3f extends Uniform { +class UniformMatrix3f extends Uniform implements IUniform { constructor(context: Context) { super(context); this.current = emptyMat3; } - // $FlowFixMe[method-unbinding] set(program: WebGLProgram, name: string, v: Float32Array): void { if (!this.fetchUniformLocation(program, name)) return; for (let i = 0; i < 9; i++) { @@ -175,13 +179,12 @@ class UniformMatrix3f extends Uniform { } const emptyMat2 = new Float32Array(4); -class UniformMatrix2f extends Uniform { +class UniformMatrix2f extends Uniform implements IUniform { constructor(context: Context) { super(context); this.current = emptyMat2; } - // $FlowFixMe[method-unbinding] set(program: WebGLProgram, name: string, v: Float32Array): void { if (!this.fetchUniformLocation(program, name)) return; for (let i = 0; i < 4; i++) { @@ -195,7 +198,6 @@ class UniformMatrix2f extends Uniform { } export { - Uniform, Uniform1i, Uniform1f, Uniform2f, @@ -207,4 +209,4 @@ export { UniformMatrix4f }; -export type UniformBindings = {[_: string]: Uniform}; +export type UniformBindings = {[_: string]: IUniform}; diff --git a/src/shaders/_prelude.fragment.glsl b/src/shaders/_prelude.fragment.glsl index 61cab6d6921..539c8afe0f5 100644 --- a/src/shaders/_prelude.fragment.glsl +++ b/src/shaders/_prelude.fragment.glsl @@ -1,23 +1,6 @@ // NOTE: This prelude is injected in the fragment shader only -#if __VERSION__ >= 300 -#define varying in -#define gl_FragColor glFragColor -#define texture2D texture -#define textureCube texture out vec4 glFragColor; -#endif - -highp vec3 hash(highp vec2 p) { - highp vec3 p3 = fract(p.xyx * vec3(443.8975, 397.2973, 491.1871)); - p3 += dot(p3, p3.yxz + 19.19); - return fract((p3.xxy + p3.yzz) * p3.zyx); -} - -vec3 dither(vec3 color, highp vec2 seed) { - vec3 rnd = hash(seed) + hash(seed + 0.59374) - 0.5; - return color + rnd / 255.0; -} highp float unpack_depth(highp vec4 rgba_depth) { @@ -56,7 +39,34 @@ vec4 applyCutout(vec4 color) { #endif } +#ifdef DEBUG_WIREFRAME + // Debug wireframe uses premultiplied alpha blending (alpha channel is left unchanged) + #define HANDLE_WIREFRAME_DEBUG \ + glFragColor = vec4(0.7, 0.0, 0.0, 0.7); \ + gl_FragDepth = gl_FragCoord.z - 0.0001; // Apply depth for wireframe overlay to reduce z-fighting +#else + #define HANDLE_WIREFRAME_DEBUG +#endif + #ifdef RENDER_CUTOFF uniform highp vec4 u_cutoff_params; -varying float v_cutoff_opacity; -#endif \ No newline at end of file +in float v_cutoff_opacity; +#endif + +// This function should be used in cases where mipmap usage is expected and +// the sampling coordinates are not continous. The lod_parameter should be +// a continous function derived from the sampling coordinates. +vec4 textureLodCustom(sampler2D image, vec2 pos, vec2 lod_coord) { + vec2 size = vec2(textureSize(image, 0)); + vec2 dx = dFdx(lod_coord.xy * size); + vec2 dy = dFdy(lod_coord.xy * size); + float delta_max_sqr = max(dot(dx, dx), dot(dy, dy)); + float lod = 0.5 * log2(delta_max_sqr); + // Note: textureLod doesn't support anisotropic filtering + // We could use textureGrad instead which supports it, but it's discouraged + // in the ARM Developer docs: + // "Do not use textureGrad() unless absolutely necessary. + // It is much slower that texture() and textureLod()..." + // https://developer.arm.com/documentation/101897/0301/Buffers-and-textures/Texture-sampling-performance + return textureLod(image, pos, lod); +} \ No newline at end of file diff --git a/src/shaders/_prelude.glsl b/src/shaders/_prelude.glsl index 4357d97b236..5574acf0053 100644 --- a/src/shaders/_prelude.glsl +++ b/src/shaders/_prelude.glsl @@ -4,46 +4,6 @@ #define EPSILON 0.0000001 #define PI 3.141592653589793 -#define EXTENT 8192.0 -#define HALF_PI PI / 2.0 -#define QUARTER_PI PI / 4.0 -#define RAD_TO_DEG 180.0 / PI -#define DEG_TO_RAD PI / 180.0 -#define GLOBE_RADIUS EXTENT / PI / 2.0 - -// linear to sRGB approximation -vec3 linearTosRGB(vec3 color) -{ - return pow(color, vec3(1./2.2)); -} - -// sRGB to linear approximation -vec3 sRGBToLinear(vec3 srgbIn) -{ - return pow(srgbIn, vec3(2.2)); -} - -// equivalent to linearTosRGB(sRGBToLinear(srgbIn) * k) -vec3 linearProduct(vec3 srgbIn, vec3 k) -{ - return srgbIn * pow(k, vec3(1./2.2)); -} - -// Apply depth for wireframe overlay to reduce z-fighting -#if __VERSION__ >= 300 - #define _HANDLE_WIREFRAME_DEPTH gl_FragDepth = gl_FragCoord.z - 0.0001; -#else - #define _HANDLE_WIREFRAME_DEPTH -#endif - -#ifdef DEBUG_WIREFRAME - // Debug wireframe uses premultiplied alpha blending (alpha channel is left unchanged) - #define HANDLE_WIREFRAME_DEBUG \ - gl_FragColor = vec4(0.7, 0.0, 0.0, 0.7); \ - _HANDLE_WIREFRAME_DEPTH; -#else - #define HANDLE_WIREFRAME_DEBUG -#endif #ifdef RENDER_CUTOFF // Calculates cutoff and fade out based on the supplied params and depth value @@ -51,10 +11,9 @@ float cutoff_opacity(vec4 cutoff_params, float depth) { float near = cutoff_params.x; float far = cutoff_params.y; float cutoffStart = cutoff_params.z; - // 0.0001 subtracted to prevent division by zero - float cutoffEnd = cutoff_params.w - 0.0001; + float cutoffEnd = cutoff_params.w; float linearDepth = (depth - near) / (far - near); return clamp((linearDepth - cutoffStart) / (cutoffEnd - cutoffStart), 0.0, 1.0); } -#endif \ No newline at end of file +#endif diff --git a/src/shaders/_prelude.vertex.glsl b/src/shaders/_prelude.vertex.glsl index e3457fe3545..4d2d06f6dd1 100644 --- a/src/shaders/_prelude.vertex.glsl +++ b/src/shaders/_prelude.vertex.glsl @@ -1,10 +1,9 @@ // NOTE: This prelude is injected in the vertex shader only -#if __VERSION__ >= 300 -#define attribute in -#define varying out -#define texture2D texture -#endif +#define EXTENT 8192.0 +#define RAD_TO_DEG 180.0 / PI +#define DEG_TO_RAD PI / 180.0 +#define GLOBE_RADIUS EXTENT / PI / 2.0 float wrap(float n, float min, float max) { float d = max - min; @@ -99,9 +98,42 @@ vec2 get_pattern_pos(const vec2 pixel_coord_upper, const vec2 pixel_coord_lower, return (tile_units_to_pixels * pos + offset) / pattern_size; } +float mercatorXfromLng(float lng) { + return (180.0 + lng) / 360.0; +} + +float mercatorYfromLat(float lat) { + return (180.0 - (RAD_TO_DEG * log(tan(PI / 4.0 + lat / 2.0 * DEG_TO_RAD)))) / 360.0; +} + +vec3 latLngToECEF(vec2 latLng) { + latLng = DEG_TO_RAD * latLng; + + float cosLat = cos(latLng[0]); + float sinLat = sin(latLng[0]); + float cosLng = cos(latLng[1]); + float sinLng = sin(latLng[1]); + + // Convert lat & lng to spherical representation. Use zoom=0 as a reference + float sx = cosLat * sinLng * GLOBE_RADIUS; + float sy = -sinLat * GLOBE_RADIUS; + float sz = cosLat * cosLng * GLOBE_RADIUS; + + return vec3(sx, sy, sz); +} + #ifdef RENDER_CUTOFF uniform vec4 u_cutoff_params; -varying float v_cutoff_opacity; +out float v_cutoff_opacity; #endif const vec4 AWAY = vec4(-1000.0, -1000.0, -1000.0, 1); // Normalized device coordinate that is not rendered. + +// Handle skirt flag for terrain & globe shaders +const float skirtOffset = 24575.0; +vec3 decomposeToPosAndSkirt(vec2 posWithComposedSkirt) +{ + float skirt = float(posWithComposedSkirt.x >= skirtOffset); + vec2 pos = posWithComposedSkirt - vec2(skirt * skirtOffset, 0.0); + return vec3(pos, skirt); +} diff --git a/src/shaders/_prelude_fog.fragment.glsl b/src/shaders/_prelude_fog.fragment.glsl index e39cf8e6c84..4a5dc474598 100644 --- a/src/shaders/_prelude_fog.fragment.glsl +++ b/src/shaders/_prelude_fog.fragment.glsl @@ -1,3 +1,14 @@ +highp vec3 hash(highp vec2 p) { + highp vec3 p3 = fract(p.xyx * vec3(443.8975, 397.2973, 491.1871)); + p3 += dot(p3, p3.yxz + 19.19); + return fract((p3.xxy + p3.yzz) * p3.zyx); +} + +vec3 dither(vec3 color, highp vec2 seed) { + vec3 rnd = hash(seed) + hash(seed + 0.59374) - 0.5; + return color + rnd / 255.0; +} + #ifdef FOG uniform mediump vec4 u_fog_color; @@ -5,7 +16,7 @@ uniform mediump vec2 u_fog_range; uniform mediump float u_fog_horizon_blend; uniform mediump vec2 u_fog_vertical_limit; uniform mediump float u_fog_temporal_offset; -varying vec3 v_fog_pos; +in vec3 v_fog_pos; uniform highp vec3 u_frustum_tl; uniform highp vec3 u_frustum_tr; @@ -64,7 +75,7 @@ float fog_opacity(vec3 pos) { return fog_opacity(fog_range(depth)); } -vec3 fog_apply(vec3 color, vec3 pos) { +vec3 fog_apply(vec3 color, vec3 pos, float opacity_limit) { float depth = length(pos); float opacity; if (u_is_globe == 1) { @@ -75,7 +86,11 @@ vec3 fog_apply(vec3 color, vec3 pos) { opacity = fog_opacity(fog_range(depth)); opacity *= fog_horizon_blending(pos / depth); } - return mix(color, u_fog_color.rgb, opacity); + return mix(color, u_fog_color.rgb, min(opacity, opacity_limit)); +} + +vec3 fog_apply(vec3 color, vec3 pos) { + return fog_apply(color, pos, 1.0); } // Apply fog computed in the vertex shader diff --git a/src/shaders/_prelude_fog.vertex.glsl b/src/shaders/_prelude_fog.vertex.glsl index 78a9788388c..e73397439e1 100644 --- a/src/shaders/_prelude_fog.vertex.glsl +++ b/src/shaders/_prelude_fog.vertex.glsl @@ -4,7 +4,7 @@ uniform mediump vec4 u_fog_color; uniform mediump vec2 u_fog_range; uniform mediump float u_fog_horizon_blend; uniform mediump mat4 u_fog_matrix; -varying vec3 v_fog_pos; +out vec3 v_fog_pos; float fog_range(float depth) { // Map [near, far] to [0, 1] without clamping diff --git a/src/shaders/_prelude_lighting.glsl b/src/shaders/_prelude_lighting.glsl index 365e3545545..41bf5b11347 100644 --- a/src/shaders/_prelude_lighting.glsl +++ b/src/shaders/_prelude_lighting.glsl @@ -33,6 +33,11 @@ float calculate_ambient_directional_factor(vec3 normal) { return vertical_factor * ambient_directional_factor; } +// equivalent to linearTosRGB(sRGBToLinear(srgbIn) * k) +vec3 linearProduct(vec3 srgbIn, vec3 k) { + return srgbIn * pow(k, vec3(1./2.2)); +} + // BEGIN Used for anisotropic ambient light // BEGIN Use with shadows, pass shadow light factor as dir_factor diff --git a/src/shaders/_prelude_raster_array.glsl b/src/shaders/_prelude_raster_array.glsl new file mode 100644 index 00000000000..8ba64822c12 --- /dev/null +++ b/src/shaders/_prelude_raster_array.glsl @@ -0,0 +1,59 @@ +#ifdef RASTER_ARRAY +uniform sampler2D u_image0; +uniform sampler2D u_image1; + +const vec4 NODATA = vec4(1); + +// Decode raster array data and interpolate linearly using nearest neighbor samples +// Returns: vec2(value, nodata_alpha) +ivec4 _raTexLinearCoord(highp vec2 texCoord, highp vec2 texResolution, out highp vec2 fxy) { + texCoord = texCoord * texResolution - 0.5; + fxy = fract(texCoord); + texCoord -= fxy; + return ivec4(texCoord.xxyy + vec2(1.5, 0.5).xyxy); +} + +vec2 _raTexLinearMix(highp vec2 fxy, highp vec4 colorMix, highp float colorOffset, highp vec4 t00, highp vec4 t10, highp vec4 t01, highp vec4 t11) { + // Interpolate as a vec2: 1) the mixed value, and 2) a binary 0/1 mask. + vec2 c00 = t00 == NODATA ? vec2(0) : vec2(colorOffset + dot(t00, colorMix), 1); + vec2 c10 = t10 == NODATA ? vec2(0) : vec2(colorOffset + dot(t10, colorMix), 1); + vec2 c01 = t01 == NODATA ? vec2(0) : vec2(colorOffset + dot(t01, colorMix), 1); + vec2 c11 = t11 == NODATA ? vec2(0) : vec2(colorOffset + dot(t11, colorMix), 1); + return mix(mix(c01, c11, fxy.x), mix(c00, c10, fxy.x), fxy.y); +} + +// Decode raster array data and interpolate linearly using nearest neighbor samples +// Returns: vec2(value, nodata_alpha) +vec2 raTexture2D_image0_linear(highp vec2 texCoord, highp vec2 texResolution, highp vec4 colorMix, highp float colorOffset) { + vec2 fxy; + ivec4 c = _raTexLinearCoord(texCoord, texResolution, fxy); + return _raTexLinearMix(fxy, colorMix, colorOffset, + texelFetch(u_image0, c.yz, 0), + texelFetch(u_image0, c.xz, 0), + texelFetch(u_image0, c.yw, 0), + texelFetch(u_image0, c.xw, 0) + ); +} +vec2 raTexture2D_image1_linear(highp vec2 texCoord, highp vec2 texResolution, highp vec4 colorMix, highp float colorOffset) { + vec2 fxy; + ivec4 c = _raTexLinearCoord(texCoord, texResolution, fxy); + return _raTexLinearMix(fxy, colorMix, colorOffset, + texelFetch(u_image1, c.yz, 0), + texelFetch(u_image1, c.xz, 0), + texelFetch(u_image1, c.yw, 0), + texelFetch(u_image1, c.xw, 0) + ); +} + +// Decode raster array data and return nearest neighbor sample +// Returns: vec2(value, nodata_alpha) +vec2 raTexture2D_image0_nearest(highp vec2 texCoord, highp vec2 texResolution, highp vec4 colorMix, highp float colorOffset) { + vec4 t = texelFetch(u_image0, ivec2(texCoord * texResolution), 0); + return t == NODATA ? vec2(0) : vec2(colorOffset + dot(t, colorMix), 1); +} +vec2 raTexture2D_image1_nearest(highp vec2 texCoord, highp vec2 texResolution, highp vec4 colorMix, highp float colorOffset) { + vec4 t = texelFetch(u_image1, ivec2(texCoord * texResolution), 0); + return t == NODATA ? vec2(0) : vec2(colorOffset + dot(t, colorMix), 1); +} + +#endif diff --git a/src/shaders/_prelude_raster_particle.glsl b/src/shaders/_prelude_raster_particle.glsl new file mode 100644 index 00000000000..05264b930a4 --- /dev/null +++ b/src/shaders/_prelude_raster_particle.glsl @@ -0,0 +1,71 @@ +#ifdef RASTER_ARRAY +uniform sampler2D u_velocity; +uniform vec2 u_velocity_res; +uniform float u_max_speed; + +const vec4 NO_DATA = vec4(1); +const vec2 INVALID_VELOCITY = vec2(-1); + +uniform vec2 u_uv_offset; +uniform float u_data_offset; +uniform vec4 u_data_scale; + +ivec4 rasterArrayLinearCoord(highp vec2 texCoord, highp vec2 texResolution, out highp vec2 fxy) { + texCoord = texCoord * texResolution - 0.5; + fxy = fract(texCoord); + texCoord -= fxy; + return ivec4(texCoord.xxyy + vec2(1.5, 0.5).xyxy); +} + +vec2 lookup_velocity(vec2 uv) { + uv = u_uv_offset.x + u_uv_offset.y * uv; + vec2 fxy; + ivec4 c = rasterArrayLinearCoord(uv, u_velocity_res, fxy); + vec4 tl = texelFetch(u_velocity, c.yz, 0); + vec4 tr = texelFetch(u_velocity, c.xz, 0); + vec4 bl = texelFetch(u_velocity, c.yw, 0); + vec4 br = texelFetch(u_velocity, c.xw, 0); + + if (tl == NO_DATA) { + return INVALID_VELOCITY; + } + if (tr == NO_DATA) { + return INVALID_VELOCITY; + } + if (bl == NO_DATA) { + return INVALID_VELOCITY; + } + if (br == NO_DATA) { + return INVALID_VELOCITY; + } + + vec4 t = mix(mix(bl, br, fxy.x), mix(tl, tr, fxy.x), fxy.y); + + vec2 velocity; +#ifdef DATA_FORMAT_UINT32 + velocity = vec2(u_data_offset + dot(t, u_data_scale), 0); +#else + velocity = vec2(u_data_offset + dot(t.rg, u_data_scale.yx), -(u_data_offset + dot(t.ba, u_data_scale.yx))); + velocity /= max(u_max_speed, length(velocity)); +#endif + return velocity; +} +#endif + +uniform highp float u_particle_pos_scale; +uniform highp vec2 u_particle_pos_offset; + +vec2 decode_pos(vec4 pixel) { + highp vec2 p = vec2( + pixel.r / 255.0 + pixel.b, + pixel.g / 255.0 + pixel.a); + + return u_particle_pos_scale * p - u_particle_pos_offset; +} + +vec4 encode_pos(vec2 pos) { + highp vec2 p = (pos + u_particle_pos_offset) / u_particle_pos_scale; + return vec4( + fract(p * 255.0), + floor(p * 255.0) / 255.0); +} diff --git a/src/shaders/_prelude_terrain.vertex.glsl b/src/shaders/_prelude_terrain.vertex.glsl index b4ea851e3ba..7fd8f974f37 100644 --- a/src/shaders/_prelude_terrain.vertex.glsl +++ b/src/shaders/_prelude_terrain.vertex.glsl @@ -24,27 +24,11 @@ vec3 elevationVector(vec2 pos) { return vec3(0, 0, 1); } #endif -// Handle skirt flag for terrain & globe shaders - -const float skirtOffset = 24575.0; -vec3 decomposeToPosAndSkirt(vec2 posWithComposedSkirt) -{ - float skirt = float(posWithComposedSkirt.x >= skirtOffset); - vec2 pos = posWithComposedSkirt - vec2(skirt * skirtOffset, 0.0); - - return vec3(pos, skirt); -} - #ifdef TERRAIN -#ifdef TERRAIN_DEM_FLOAT_FORMAT -uniform highp sampler2D u_dem; -uniform highp sampler2D u_dem_prev; -#else uniform highp sampler2D u_dem; uniform highp sampler2D u_dem_prev; -#endif -uniform vec4 u_dem_unpack; + uniform vec2 u_dem_tl; uniform vec2 u_dem_tl_prev; uniform float u_dem_scale; @@ -64,10 +48,6 @@ vec4 tileUvToDemSample(vec2 uv, float dem_size, float dem_scale, vec2 dem_tl) { return vec4((pos - f + 0.5) / (dem_size + 2.0), f); } -float decodeElevation(vec4 v) { - return dot(vec4(v.xyz * 255.0, -1.0), u_dem_unpack); -} - float currentElevation(vec2 apos) { #ifdef TERRAIN_DEM_FLOAT_FORMAT vec2 pos = (u_dem_size * (apos / 8192.0 * u_dem_scale + u_dem_tl) + 1.5) / (u_dem_size + 2.0); @@ -78,10 +58,10 @@ float currentElevation(vec2 apos) { vec2 pos = r.xy; vec2 f = r.zw; - float tl = decodeElevation(texture2D(u_dem, pos)); - float tr = decodeElevation(texture2D(u_dem, pos + vec2(dd, 0.0))); - float bl = decodeElevation(texture2D(u_dem, pos + vec2(0.0, dd))); - float br = decodeElevation(texture2D(u_dem, pos + vec2(dd, dd))); + float tl = texture(u_dem, pos).r; + float tr = texture(u_dem, pos + vec2(dd, 0)).r; + float bl = texture(u_dem, pos + vec2(0, dd)).r; + float br = texture(u_dem, pos + vec2(dd, dd)).r; return u_exaggeration * mix(mix(tl, tr, f.x), mix(bl, br, f.x), f.y); #endif @@ -97,10 +77,10 @@ float prevElevation(vec2 apos) { vec2 pos = r.xy; vec2 f = r.zw; - float tl = decodeElevation(texture2D(u_dem_prev, pos)); - float tr = decodeElevation(texture2D(u_dem_prev, pos + vec2(dd, 0.0))); - float bl = decodeElevation(texture2D(u_dem_prev, pos + vec2(0.0, dd))); - float br = decodeElevation(texture2D(u_dem_prev, pos + vec2(dd, dd))); + float tl = texture(u_dem_prev, pos).r; + float tr = texture(u_dem_prev, pos + vec2(dd, 0)).r; + float bl = texture(u_dem_prev, pos + vec2(0, dd)).r; + float br = texture(u_dem_prev, pos + vec2(dd, dd)).r; return u_exaggeration * mix(mix(tl, tr, f.x), mix(bl, br, f.x), f.y); #endif @@ -135,7 +115,7 @@ highp float unpack_depth(highp vec4 rgba_depth) bool isOccluded(vec4 frag) { vec3 coord = frag.xyz / frag.w; - float depth = unpack_depth(texture2D(u_depth, (coord.xy + 1.0) * 0.5)); + float depth = unpack_depth(texture(u_depth, (coord.xy + 1.0) * 0.5)); return coord.z > depth + 0.0005; } @@ -145,10 +125,10 @@ float occlusionFade(vec4 frag) { vec3 df = vec3(5.0 * u_depth_size_inv, 0.0); vec2 uv = 0.5 * coord.xy + 0.5; vec4 depth = vec4( - unpack_depth(texture2D(u_depth, uv - df.xz)), - unpack_depth(texture2D(u_depth, uv + df.xz)), - unpack_depth(texture2D(u_depth, uv - df.zy)), - unpack_depth(texture2D(u_depth, uv + df.zy)) + unpack_depth(texture(u_depth, uv - df.xz)), + unpack_depth(texture(u_depth, uv + df.xz)), + unpack_depth(texture(u_depth, uv - df.zy)), + unpack_depth(texture(u_depth, uv + df.zy)) ); return dot(vec4(0.25), vec4(1.0) - clamp(300.0 * (vec4(coord.z - 0.001) - depth), 0.0, 1.0)); } @@ -158,21 +138,10 @@ float occlusionFade(vec4 frag) { // This is so that rendering changes are reflected on CPU side for feature querying. vec4 fourSample(vec2 pos, vec2 off) { -#ifdef TERRAIN_DEM_FLOAT_FORMAT float tl = texture(u_dem, pos).r; float tr = texture(u_dem, pos + vec2(off.x, 0.0)).r; float bl = texture(u_dem, pos + vec2(0.0, off.y)).r; float br = texture(u_dem, pos + off).r; -#else - vec4 demtl = vec4(texture2D(u_dem, pos).xyz * 255.0, -1.0); - float tl = dot(demtl, u_dem_unpack); - vec4 demtr = vec4(texture2D(u_dem, pos + vec2(off.x, 0.0)).xyz * 255.0, -1.0); - float tr = dot(demtr, u_dem_unpack); - vec4 dembl = vec4(texture2D(u_dem, pos + vec2(0.0, off.y)).xyz * 255.0, -1.0); - float bl = dot(dembl, u_dem_unpack); - vec4 dembr = vec4(texture2D(u_dem, pos + off).xyz * 255.0, -1.0); - float br = dot(dembr, u_dem_unpack); -#endif return vec4(tl, tr, bl, br); } diff --git a/src/shaders/atmosphere.fragment.glsl b/src/shaders/atmosphere.fragment.glsl index f89a5e126b0..ca0375b2f55 100644 --- a/src/shaders/atmosphere.fragment.glsl +++ b/src/shaders/atmosphere.fragment.glsl @@ -10,8 +10,8 @@ uniform vec4 u_space_color; uniform float u_horizon_angle; -varying highp vec3 v_ray_dir; -varying highp vec3 v_horizon_dir; +in highp vec3 v_ray_dir; +in highp vec3 v_horizon_dir; void main() { highp vec3 dir = normalize(v_ray_dir); @@ -26,14 +26,14 @@ void main() { // antialiasing that might be applied from globe_raster.fragment.glsl if (norm_dist_from_center < 0.98) { #ifdef ALPHA_PASS - gl_FragColor = vec4(0, 0, 0, 0); + glFragColor = vec4(0, 0, 0, 0); return; #else #ifdef NATIVE // Needed for render test parity since white canvas is assumed - gl_FragColor = vec4(1, 1, 1, 1); + glFragColor = vec4(1, 1, 1, 1); #else - gl_FragColor = vec4(0, 0, 0, 1); + glFragColor = vec4(0, 0, 0, 1); #endif return; #endif @@ -87,7 +87,7 @@ void main() { float a2 = mix(a0, a1, t); float a = mix(alpha_2, a2, t); - gl_FragColor = vec4(1.0, 1.0, 1.0, a); + glFragColor = vec4(1.0, 1.0, 1.0, a); #else vec3 c0 = mix(color_stop_2, color_stop_1, alpha_1); vec3 c1 = mix(c0, color_stop_0, alpha_0); @@ -103,6 +103,6 @@ void main() { #endif // Blending with background space color - gl_FragColor = vec4(c * t, t); + glFragColor = vec4(c * t, t); #endif } diff --git a/src/shaders/atmosphere.vertex.glsl b/src/shaders/atmosphere.vertex.glsl index a3336b3236b..61edafa1217 100644 --- a/src/shaders/atmosphere.vertex.glsl +++ b/src/shaders/atmosphere.vertex.glsl @@ -1,5 +1,5 @@ -attribute vec3 a_pos; -attribute vec2 a_uv; +in vec3 a_pos; +in vec2 a_uv; // View frustum direction vectors pointing from the camera position to of each the corner points uniform vec3 u_frustum_tl; @@ -8,8 +8,8 @@ uniform vec3 u_frustum_br; uniform vec3 u_frustum_bl; uniform float u_horizon; -varying highp vec3 v_ray_dir; -varying highp vec3 v_horizon_dir; +out highp vec3 v_ray_dir; +out highp vec3 v_horizon_dir; void main() { v_ray_dir = mix( diff --git a/src/shaders/background.fragment.glsl b/src/shaders/background.fragment.glsl index 81a21eab09e..b4d61edae78 100644 --- a/src/shaders/background.fragment.glsl +++ b/src/shaders/background.fragment.glsl @@ -5,7 +5,7 @@ uniform vec4 u_color; uniform float u_opacity; #ifdef LIGHTING_3D_MODE -varying vec4 v_color; +in vec4 v_color; #endif void main() { @@ -19,10 +19,10 @@ void main() { out_color = fog_dither(fog_apply_premultiplied(out_color, v_fog_pos)); #endif - gl_FragColor = out_color * u_opacity; + glFragColor = out_color * u_opacity; #ifdef OVERDRAW_INSPECTOR - gl_FragColor = vec4(1.0); + glFragColor = vec4(1.0); #endif HANDLE_WIREFRAME_DEBUG; diff --git a/src/shaders/background.vertex.glsl b/src/shaders/background.vertex.glsl index 0901475093b..6cc521bd143 100644 --- a/src/shaders/background.vertex.glsl +++ b/src/shaders/background.vertex.glsl @@ -1,13 +1,13 @@ #include "_prelude_fog.vertex.glsl" #include "_prelude_lighting.glsl" -attribute vec2 a_pos; +in vec2 a_pos; uniform mat4 u_matrix; #ifdef LIGHTING_3D_MODE uniform mediump vec4 u_color; -varying vec4 v_color; +out vec4 v_color; uniform float u_emissive_strength; #endif diff --git a/src/shaders/background_pattern.fragment.glsl b/src/shaders/background_pattern.fragment.glsl index 62601ae0dc1..a6871dbb5bf 100644 --- a/src/shaders/background_pattern.fragment.glsl +++ b/src/shaders/background_pattern.fragment.glsl @@ -9,12 +9,12 @@ uniform float u_emissive_strength; uniform sampler2D u_image; -varying vec2 v_pos; +in vec2 v_pos; void main() { vec2 imagecoord = mod(v_pos, 1.0); vec2 pos = mix(u_pattern_tl / u_texsize, u_pattern_br / u_texsize, imagecoord); - vec4 out_color = texture2D(u_image, pos); + vec4 out_color = textureLodCustom(u_image, pos, v_pos); #ifdef LIGHTING_3D_MODE out_color = apply_lighting_with_emission_ground(out_color, u_emissive_strength); @@ -23,10 +23,10 @@ void main() { out_color = fog_dither(fog_apply_premultiplied(out_color, v_fog_pos)); #endif - gl_FragColor = out_color * u_opacity; + glFragColor = out_color * u_opacity; #ifdef OVERDRAW_INSPECTOR - gl_FragColor = vec4(1.0); + glFragColor = vec4(1.0); #endif HANDLE_WIREFRAME_DEBUG; diff --git a/src/shaders/background_pattern.vertex.glsl b/src/shaders/background_pattern.vertex.glsl index c49cb53c4ba..2cf40e743de 100644 --- a/src/shaders/background_pattern.vertex.glsl +++ b/src/shaders/background_pattern.vertex.glsl @@ -6,9 +6,9 @@ uniform vec2 u_pixel_coord_upper; uniform vec2 u_pixel_coord_lower; uniform float u_tile_units_to_pixels; -attribute vec2 a_pos; +in vec2 a_pos; -varying vec2 v_pos; +out vec2 v_pos; void main() { gl_Position = u_matrix * vec4(a_pos, 0, 1); diff --git a/src/shaders/circle.fragment.glsl b/src/shaders/circle.fragment.glsl index 34f7cbe0a74..1cc124f5f3a 100644 --- a/src/shaders/circle.fragment.glsl +++ b/src/shaders/circle.fragment.glsl @@ -1,8 +1,8 @@ #include "_prelude_fog.fragment.glsl" #include "_prelude_lighting.glsl" -varying vec3 v_data; -varying float v_visibility; +in vec3 v_data; +in float v_visibility; #pragma mapbox: define highp vec4 color #pragma mapbox: define mediump float radius @@ -45,9 +45,9 @@ void main() { out_color = fog_apply_premultiplied(out_color, v_fog_pos); #endif - gl_FragColor = out_color * (v_visibility * opacity_t); + glFragColor = out_color * (v_visibility * opacity_t); #ifdef OVERDRAW_INSPECTOR - gl_FragColor = vec4(1.0); + glFragColor = vec4(1.0); #endif } diff --git a/src/shaders/circle.vertex.glsl b/src/shaders/circle.vertex.glsl index b804295b038..726112780bf 100644 --- a/src/shaders/circle.vertex.glsl +++ b/src/shaders/circle.vertex.glsl @@ -12,11 +12,11 @@ uniform mat2 u_extrude_scale; uniform lowp float u_device_pixel_ratio; uniform highp float u_camera_to_center_distance; -attribute vec2 a_pos; +in vec2 a_pos; #ifdef PROJECTION_GLOBE_VIEW -attribute vec3 a_pos_3; // Projected position on the globe -attribute vec3 a_pos_normal_3; // Surface normal at the position +in vec3 a_pos_3; // Projected position on the globe +in vec3 a_pos_normal_3; // Surface normal at the position // Uniforms required for transition between globe and mercator uniform mat4 u_inv_rot_matrix; @@ -26,8 +26,8 @@ uniform float u_zoom_transition; uniform vec3 u_up_dir; #endif -varying vec3 v_data; -varying float v_visibility; +out vec3 v_data; +out float v_visibility; #pragma mapbox: define highp vec4 color #pragma mapbox: define mediump float radius diff --git a/src/shaders/clipping_mask.fragment.glsl b/src/shaders/clipping_mask.fragment.glsl index fdd1a9285a6..c453278a792 100644 --- a/src/shaders/clipping_mask.fragment.glsl +++ b/src/shaders/clipping_mask.fragment.glsl @@ -1,3 +1,3 @@ void main() { - gl_FragColor = vec4(1.0); + glFragColor = vec4(1.0); } diff --git a/src/shaders/clipping_mask.vertex.glsl b/src/shaders/clipping_mask.vertex.glsl index 866c3cd2f39..46f9eaa1244 100644 --- a/src/shaders/clipping_mask.vertex.glsl +++ b/src/shaders/clipping_mask.vertex.glsl @@ -1,4 +1,4 @@ -attribute vec2 a_pos; +in vec2 a_pos; uniform mat4 u_matrix; diff --git a/src/shaders/collision_box.fragment.glsl b/src/shaders/collision_box.fragment.glsl index f53471da06d..63a95b9aa2e 100644 --- a/src/shaders/collision_box.fragment.glsl +++ b/src/shaders/collision_box.fragment.glsl @@ -1,10 +1,10 @@ -varying float v_placed; -varying float v_notUsed; +in float v_placed; +in float v_notUsed; void main() { vec4 red = vec4(1.0, 0.0, 0.0, 1.0); // Red = collision, hide label vec4 blue = vec4(0.0, 0.0, 1.0, 0.5); // Blue = no collision, label is showing - gl_FragColor = mix(red, blue, step(0.5, v_placed)) * 0.5; - gl_FragColor *= mix(1.0, 0.1, step(0.5, v_notUsed)); -} \ No newline at end of file + glFragColor = mix(red, blue, step(0.5, v_placed)) * 0.5; + glFragColor *= mix(1.0, 0.1, step(0.5, v_notUsed)); +} diff --git a/src/shaders/collision_box.vertex.glsl b/src/shaders/collision_box.vertex.glsl index 3d8b8e95bf4..12e2718fe87 100644 --- a/src/shaders/collision_box.vertex.glsl +++ b/src/shaders/collision_box.vertex.glsl @@ -1,22 +1,23 @@ #include "_prelude_terrain.vertex.glsl" -attribute vec3 a_pos; -attribute vec2 a_anchor_pos; -attribute vec2 a_extrude; -attribute vec2 a_placed; -attribute vec2 a_shift; -attribute float a_size_scale; -attribute vec2 a_padding; +in vec3 a_pos; +in vec2 a_anchor_pos; +in vec2 a_extrude; +in vec2 a_placed; +in vec2 a_shift; +in float a_size_scale; +in vec2 a_padding; +in float a_z_offset; uniform mat4 u_matrix; uniform vec2 u_extrude_scale; uniform float u_camera_to_center_distance; -varying float v_placed; -varying float v_notUsed; +out float v_placed; +out float v_notUsed; void main() { - vec4 projectedPoint = u_matrix * vec4(a_pos + elevationVector(a_anchor_pos) * elevation(a_anchor_pos), 1); + vec4 projectedPoint = u_matrix * vec4(a_pos + elevationVector(a_anchor_pos) * (a_z_offset + elevation(a_anchor_pos)), 1); highp float camera_to_anchor_distance = projectedPoint.w; highp float collision_perspective_ratio = clamp( diff --git a/src/shaders/collision_circle.fragment.glsl b/src/shaders/collision_circle.fragment.glsl index 3cd66b1155e..67bd43a0815 100644 --- a/src/shaders/collision_circle.fragment.glsl +++ b/src/shaders/collision_circle.fragment.glsl @@ -1,7 +1,7 @@ -varying float v_radius; -varying vec2 v_extrude; -varying float v_perspective_ratio; -varying float v_collision; +in float v_radius; +in vec2 v_extrude; +in float v_perspective_ratio; +in float v_collision; void main() { float alpha = 0.5 * min(v_perspective_ratio, 1.0); @@ -13,5 +13,5 @@ void main() { vec4 color = mix(vec4(0.0, 0.0, 1.0, 0.5), vec4(1.0, 0.0, 0.0, 1.0), v_collision); - gl_FragColor = color * alpha * opacity_t; + glFragColor = color * alpha * opacity_t; } diff --git a/src/shaders/collision_circle.vertex.glsl b/src/shaders/collision_circle.vertex.glsl index 764dd900aa6..1af105a127b 100644 --- a/src/shaders/collision_circle.vertex.glsl +++ b/src/shaders/collision_circle.vertex.glsl @@ -1,16 +1,16 @@ -attribute vec2 a_pos_2f; -attribute float a_radius; -attribute vec2 a_flags; +in vec2 a_pos_2f; +in float a_radius; +in vec2 a_flags; uniform mat4 u_matrix; uniform mat4 u_inv_matrix; uniform vec2 u_viewport_size; uniform float u_camera_to_center_distance; -varying float v_radius; -varying vec2 v_extrude; -varying float v_perspective_ratio; -varying float v_collision; +out float v_radius; +out vec2 v_extrude; +out float v_perspective_ratio; +out float v_collision; vec3 toTilePosition(vec2 screenPos) { // Shoot a ray towards the ground to reconstruct the depth-value diff --git a/src/shaders/debug.fragment.glsl b/src/shaders/debug.fragment.glsl index c15a694bd1b..f337ad66c26 100644 --- a/src/shaders/debug.fragment.glsl +++ b/src/shaders/debug.fragment.glsl @@ -1,9 +1,9 @@ uniform highp vec4 u_color; uniform sampler2D u_overlay; -varying vec2 v_uv; +in vec2 v_uv; void main() { - vec4 overlay_color = texture2D(u_overlay, v_uv); - gl_FragColor = mix(u_color, overlay_color, overlay_color.a); + vec4 overlay_color = texture(u_overlay, v_uv); + glFragColor = mix(u_color, overlay_color, overlay_color.a); } diff --git a/src/shaders/debug.vertex.glsl b/src/shaders/debug.vertex.glsl index 570a4a76782..ef8b6ed9661 100644 --- a/src/shaders/debug.vertex.glsl +++ b/src/shaders/debug.vertex.glsl @@ -1,10 +1,10 @@ #include "_prelude_terrain.vertex.glsl" -attribute vec2 a_pos; +in vec2 a_pos; #ifdef PROJECTION_GLOBE_VIEW -attribute vec3 a_pos_3; +in vec3 a_pos_3; #endif -varying vec2 v_uv; +out vec2 v_uv; uniform mat4 u_matrix; uniform float u_overlay_scale; diff --git a/src/shaders/fill.fragment.glsl b/src/shaders/fill.fragment.glsl index a6462013962..ded717f1e52 100644 --- a/src/shaders/fill.fragment.glsl +++ b/src/shaders/fill.fragment.glsl @@ -19,10 +19,10 @@ void main() { out_color = fog_dither(fog_apply_premultiplied(out_color, v_fog_pos)); #endif - gl_FragColor = out_color * opacity; + glFragColor = out_color * opacity; #ifdef OVERDRAW_INSPECTOR - gl_FragColor = vec4(1.0); + glFragColor = vec4(1.0); #endif HANDLE_WIREFRAME_DEBUG; diff --git a/src/shaders/fill.vertex.glsl b/src/shaders/fill.vertex.glsl index 5b140beb0e2..fa497509e78 100644 --- a/src/shaders/fill.vertex.glsl +++ b/src/shaders/fill.vertex.glsl @@ -1,6 +1,6 @@ #include "_prelude_fog.vertex.glsl" -attribute vec2 a_pos; +in vec2 a_pos; uniform mat4 u_matrix; diff --git a/src/shaders/fill_extrusion.fragment.glsl b/src/shaders/fill_extrusion.fragment.glsl index 1a04c2ab228..cc3ad2ec3c6 100644 --- a/src/shaders/fill_extrusion.fragment.glsl +++ b/src/shaders/fill_extrusion.fragment.glsl @@ -2,27 +2,27 @@ #include "_prelude_shadow.fragment.glsl" #include "_prelude_lighting.glsl" -varying vec4 v_color; +in vec4 v_color; +in vec4 v_flat; #ifdef RENDER_SHADOWS -varying highp vec4 v_pos_light_view_0; -varying highp vec4 v_pos_light_view_1; -varying float v_depth; +in highp vec4 v_pos_light_view_0; +in highp vec4 v_pos_light_view_1; #endif uniform lowp float u_opacity; #ifdef FAUX_AO uniform lowp vec2 u_ao; -varying vec2 v_ao; +in vec2 v_ao; #endif #if defined(ZERO_ROOF_RADIUS) && !defined(LIGHTING_3D_MODE) -varying vec4 v_roof_color; +in vec4 v_roof_color; #endif #if defined(ZERO_ROOF_RADIUS) || defined(RENDER_SHADOWS) || defined(LIGHTING_3D_MODE) -varying highp vec3 v_normal; +in highp vec3 v_normal; #endif uniform vec3 u_flood_light_color; @@ -31,11 +31,13 @@ uniform float u_flood_light_intensity; uniform vec3 u_ground_shadow_factor; #if defined(LIGHTING_3D_MODE) && defined(FLOOD_LIGHT) -varying float v_flood_radius; -varying float v_has_floodlight; +in float v_flood_radius; +in float v_has_floodlight; #endif -varying float v_height; +uniform float u_emissive_strength; + +in float v_height; void main() { @@ -91,7 +93,7 @@ float flood_radiance = 0.0; #ifdef FLOOD_LIGHT float ndotl_unclamped = dot(normal, u_shadow_direction); float ndotl = max(0.0, ndotl_unclamped); - float occlusion = ndotl_unclamped < 0.0 ? 1.0 : shadow_occlusion(ndotl, v_pos_light_view_0, v_pos_light_view_1, v_depth); + float occlusion = ndotl_unclamped < 0.0 ? 1.0 : shadow_occlusion(ndotl, v_pos_light_view_0, v_pos_light_view_1, 1.0 / gl_FragCoord.w); // Compute both FE and flood lights separately and interpolate between the two. // "litColor" uses pretty much "shadowed_light_factor_normal" as the directional component. @@ -100,7 +102,7 @@ float flood_radiance = 0.0; color.rgb = mix(litColor, floodLitColor, flood_radiance); #else // FLOOD_LIGHT - float shadowed_lighting_factor = shadowed_light_factor_normal(normal, v_pos_light_view_0, v_pos_light_view_1, v_depth); + float shadowed_lighting_factor = shadowed_light_factor_normal(normal, v_pos_light_view_0, v_pos_light_view_1, 1.0 / gl_FragCoord.w); color.rgb = apply_lighting(color.rgb, normal, shadowed_lighting_factor); #endif // !FLOOD_LIGHT #else // RENDER_SHADOWS @@ -110,6 +112,7 @@ float flood_radiance = 0.0; #endif // FLOOD_LIGHT #endif // !RENDER_SHADOWS + color.rgb = mix(color.rgb, v_flat.rgb, u_emissive_strength); color *= u_opacity; #endif // LIGHTING_3D_MODE @@ -117,18 +120,14 @@ float flood_radiance = 0.0; color = fog_dither(fog_apply_premultiplied(color, v_fog_pos, h)); #endif -#ifdef RENDER_CUTOFF - color *= v_cutoff_opacity; -#endif - #ifdef INDICATOR_CUTOUT color = applyCutout(color); #endif - gl_FragColor = color; + glFragColor = color; #ifdef OVERDRAW_INSPECTOR - gl_FragColor = vec4(1.0); + glFragColor = vec4(1.0); #endif HANDLE_WIREFRAME_DEBUG; diff --git a/src/shaders/fill_extrusion.vertex.glsl b/src/shaders/fill_extrusion.vertex.glsl index aa8aae469d9..6e2a3ee9168 100644 --- a/src/shaders/fill_extrusion.vertex.glsl +++ b/src/shaders/fill_extrusion.vertex.glsl @@ -3,12 +3,6 @@ #include "_prelude_shadow.vertex.glsl" #include "_prelude_lighting.glsl" -#if __VERSION__ >= 300 -#ifdef RENDER_CUTOFF -invariant gl_Position; -#endif -#endif - uniform mat4 u_matrix; uniform vec3 u_lightcolor; uniform lowp vec3 u_lightpos; @@ -17,12 +11,12 @@ uniform float u_vertical_gradient; uniform lowp float u_opacity; uniform float u_edge_radius; -attribute vec4 a_pos_normal_ed; -attribute vec2 a_centroid_pos; +in vec4 a_pos_normal_ed; +in vec2 a_centroid_pos; #ifdef PROJECTION_GLOBE_VIEW -attribute vec3 a_pos_3; // Projected position on the globe -attribute vec3 a_pos_normal_3; // Surface normal at the position +in vec3 a_pos_3; // Projected position on the globe +in vec3 a_pos_normal_3; // Surface normal at the position uniform mat4 u_inv_rot_matrix; uniform vec2 u_merc_center; @@ -34,36 +28,37 @@ uniform float u_height_lift; uniform highp float u_vertical_scale; -varying vec4 v_color; +out vec4 v_color; +out vec4 v_flat; #ifdef RENDER_SHADOWS uniform mat4 u_light_matrix_0; uniform mat4 u_light_matrix_1; -varying highp vec4 v_pos_light_view_0; -varying highp vec4 v_pos_light_view_1; -varying float v_depth; +out highp vec4 v_pos_light_view_0; +out highp vec4 v_pos_light_view_1; +out float v_depth; #endif #if defined(ZERO_ROOF_RADIUS) && !defined(LIGHTING_3D_MODE) -varying vec4 v_roof_color; +out vec4 v_roof_color; #endif #if defined(ZERO_ROOF_RADIUS) || defined(RENDER_SHADOWS) || defined(LIGHTING_3D_MODE) -varying highp vec3 v_normal; +out highp vec3 v_normal; #endif #ifdef FAUX_AO uniform lowp vec2 u_ao; -varying vec2 v_ao; +out vec2 v_ao; #endif #if defined(LIGHTING_3D_MODE) && defined(FLOOD_LIGHT) -varying float v_flood_radius; -varying float v_has_floodlight; +out float v_flood_radius; +out float v_has_floodlight; #endif -varying float v_height; +out float v_height; #pragma mapbox: define highp float base #pragma mapbox: define highp float height @@ -97,6 +92,7 @@ void main() { base = max(0.0, base); + float attr_height = height; height = max(0.0, top_up_ny.y == 0.0 && top_up_ny.x == 1.0 ? height - u_edge_radius : height); float t = top_up_ny.x; @@ -108,7 +104,7 @@ void main() { float ele = 0.0; float h = 0.0; - float c_ele; + float c_ele = 0.0; vec3 pos; #ifdef TERRAIN bool flat_roof = centroid_pos.x != 0.0 && t > 0.0; @@ -132,8 +128,28 @@ void main() { pos = mix_globe_mercator(globe_pos, merc_pos, u_zoom_transition); #endif - float hidden = float(centroid_pos.x == 0.0 && centroid_pos.y == 1.0); - gl_Position = mix(u_matrix * vec4(pos, 1), AWAY, hidden); + float cutoff = 1.0; + vec3 scaled_pos = pos; +#ifdef RENDER_CUTOFF + vec3 centroid_random = vec3(centroid_pos.xy, centroid_pos.x + centroid_pos.y + 1.0); + vec3 ground_pos = centroid_pos.x == 0.0 ? pos.xyz : (centroid_random / 8.0); + vec4 ground = u_matrix * vec4(ground_pos.xy, ele, 1.0); + cutoff = max(0.01, cutoff_opacity(u_cutoff_params, ground.z)); + if (centroid_pos.y != 0.0 && centroid_pos.x != 0.0) { + vec3 g = floor(ground_pos); + vec3 mod_ = centroid_random - g * 8.0; + float seed = min(1.0, 0.1 * (min(3.5, max(mod_.x + mod_.y, 0.2 * attr_height)) * 0.35 + mod_.z)); + if (cutoff < 0.8 - seed) { + cutoff = 0.0; + } + } + float cutoff_scale = cutoff; + + scaled_pos.z = mix(c_ele, h, cutoff_scale); +#endif + float hidden = float((centroid_pos.x == 0.0 && centroid_pos.y == 1.0) || (cutoff < 0.01 && centroid_pos.x != 0.0)); + + gl_Position = mix(u_matrix * vec4(scaled_pos, 1), AWAY, hidden); h = h - ele; v_height = h; @@ -147,7 +163,6 @@ void main() { #endif v_pos_light_view_0 = u_light_matrix_0 * vec4(shd_pos0, 1); v_pos_light_view_1 = u_light_matrix_1 * vec4(shd_pos1, 1); - v_depth = gl_Position.w; #endif float NdotL = 0.0; @@ -209,7 +224,7 @@ void main() { #endif // FLOOD_LIGHT v_color = vec4(color.rgb, 1.0); - + v_flat = vec4(linearProduct(color.rgb, vec3(calculate_NdotL(normal))), 1.0); #else // LIGHTING_3D_MODE // Assign final color based on surface + ambient light color, diffuse light NdotL, and light color // with lower bounds adjusted to hue of light @@ -230,8 +245,4 @@ void main() { #ifdef FOG v_fog_pos = fog_position(pos); #endif - -#ifdef RENDER_CUTOFF - v_cutoff_opacity = cutoff_opacity(u_cutoff_params, gl_Position.z); -#endif } diff --git a/src/shaders/fill_extrusion_ground_effect.fragment.glsl b/src/shaders/fill_extrusion_ground_effect.fragment.glsl index a753363ca02..cda111a285a 100644 --- a/src/shaders/fill_extrusion_ground_effect.fragment.glsl +++ b/src/shaders/fill_extrusion_ground_effect.fragment.glsl @@ -10,10 +10,10 @@ uniform sampler2D u_fb; uniform float u_fb_size; #ifdef SDF_SUBPASS -varying highp vec2 v_pos; -varying highp vec4 v_line_segment; -varying highp float v_flood_light_radius_tile; -varying highp vec2 v_ao; +in highp vec2 v_pos; +in highp vec4 v_line_segment; +in highp float v_flood_light_radius_tile; +in highp vec2 v_ao; float line_df(highp vec2 a, highp vec2 b, highp vec2 p) { highp vec2 ba = b - a; @@ -23,7 +23,7 @@ float line_df(highp vec2 a, highp vec2 b, highp vec2 p) { } #ifdef FOG -varying highp float v_fog; +in highp float v_fog; #endif // FOG #endif // SDF_SUBPASS @@ -36,9 +36,9 @@ void main() { #ifdef CLEAR_SUBPASS vec4 color = vec4(1.0); #ifdef CLEAR_FROM_TEXTURE - color = texture2D(u_fb, gl_FragCoord.xy / vec2(u_fb_size)); + color = texture(u_fb, gl_FragCoord.xy / vec2(u_fb_size)); #endif // CLEAR_FROM_TEXTURE - gl_FragColor = color; + glFragColor = color; #else // CLEAR_SUBPASS #ifdef SDF_SUBPASS highp float d = line_df(v_line_segment.xy, v_line_segment.zw, v_pos); @@ -54,14 +54,14 @@ void main() { #ifdef RENDER_CUTOFF fog *= v_cutoff_opacity; #endif // RENDER_CUTOFF - gl_FragColor = vec4(vec3(0.0), mix(1.0, d, effect_intensity * u_opacity * fog)); + glFragColor = vec4(vec3(0.0), mix(1.0, d, effect_intensity * u_opacity * fog)); #else // SDF_SUBPASS vec4 color = mix(vec4(u_flood_light_color, 1.0), vec4(vec3(0.0), 1.0), u_ao_pass); #ifdef OVERDRAW_INSPECTOR color = vec4(1.0); #endif - gl_FragColor = color; - HANDLE_WIREFRAME_DEBUG; + glFragColor = color; #endif // !SDF_SUBPASS +HANDLE_WIREFRAME_DEBUG; #endif // !CLEAR_SUBPASS } diff --git a/src/shaders/fill_extrusion_ground_effect.vertex.glsl b/src/shaders/fill_extrusion_ground_effect.vertex.glsl index 276fe4ea536..ce886490bf7 100644 --- a/src/shaders/fill_extrusion_ground_effect.vertex.glsl +++ b/src/shaders/fill_extrusion_ground_effect.vertex.glsl @@ -1,16 +1,16 @@ #include "_prelude_fog.vertex.glsl" -attribute highp vec4 a_pos_end; -attribute highp float a_angular_offset_factor; -attribute highp float a_hidden_by_landmark; +in highp vec4 a_pos_end; +in highp float a_angular_offset_factor; +in highp float a_hidden_by_landmark; #ifdef SDF_SUBPASS -varying highp vec2 v_pos; -varying highp vec4 v_line_segment; -varying highp float v_flood_light_radius_tile; -varying highp vec2 v_ao; +out highp vec2 v_pos; +out highp vec4 v_line_segment; +out highp float v_flood_light_radius_tile; +out highp vec2 v_ao; #ifdef FOG -varying highp float v_fog; +out highp float v_fog; #endif #endif @@ -36,16 +36,15 @@ void main() { vec2 start_bottom = a_pos_end.zw - q * 2.0; float fl_ground_radius = flood_light_ground_radius; -#ifdef FORCE_ABS_FL_GROUND_RADIUS fl_ground_radius = abs(flood_light_ground_radius); -#endif + float direction = flood_light_ground_radius < 0.0 ? -1.0 : 1.0; float flood_radius_tile = fl_ground_radius * u_meter_to_tile; vec2 v = normalize(q - p); float ao_radius = u_ao.y / 3.5; // adjust AO radius slightly float effect_radius = mix(flood_radius_tile, ao_radius, u_ao_pass) + u_edge_radius; float angular_offset_factor = a_angular_offset_factor / NORM * TANGENT_CUTOFF; - float angular_offset = angular_offset_factor * effect_radius; + float angular_offset = direction * angular_offset_factor * effect_radius; float top = 1.0 - start_bottom.y; @@ -53,7 +52,7 @@ void main() { vec2 extrusion_parallel = v * side * mix(1.0, angular_offset, top); vec2 perp = vec2(v.y, -v.x); - vec2 extrusion_perp = perp * effect_radius * top; + vec2 extrusion_perp = direction * perp * effect_radius * top; vec3 pos = vec3(mix(q, p, start_bottom.x), 0.0); pos.xy += extrusion_parallel + extrusion_perp; diff --git a/src/shaders/fill_extrusion_pattern.fragment.glsl b/src/shaders/fill_extrusion_pattern.fragment.glsl index d72ba3f452f..fdca6826c04 100644 --- a/src/shaders/fill_extrusion_pattern.fragment.glsl +++ b/src/shaders/fill_extrusion_pattern.fragment.glsl @@ -7,15 +7,15 @@ uniform sampler2D u_image; #ifdef FAUX_AO uniform lowp vec2 u_ao; -varying vec3 v_ao; +in vec3 v_ao; #endif #ifdef LIGHTING_3D_MODE -varying vec3 v_normal; +in vec3 v_normal; #endif -varying vec2 v_pos; -varying vec4 v_lighting; +in vec2 v_pos; +in vec4 v_lighting; uniform lowp float u_opacity; @@ -35,7 +35,8 @@ void main() { vec2 imagecoord = mod(v_pos, 1.0); vec2 pos = mix(pattern_tl / u_texsize, pattern_br / u_texsize, imagecoord); - vec4 out_color = texture2D(u_image, pos); + vec2 lod_pos = mix(pattern_tl / u_texsize, pattern_br / u_texsize, v_pos); + vec4 out_color = textureLodCustom(u_image, pos, lod_pos); #ifdef LIGHTING_3D_MODE out_color = apply_lighting(out_color, normalize(v_normal)) * u_opacity; @@ -63,10 +64,10 @@ void main() { out_color = applyCutout(out_color); #endif - gl_FragColor = out_color; + glFragColor = out_color; #ifdef OVERDRAW_INSPECTOR - gl_FragColor = vec4(1.0); + glFragColor = vec4(1.0); #endif HANDLE_WIREFRAME_DEBUG; diff --git a/src/shaders/fill_extrusion_pattern.vertex.glsl b/src/shaders/fill_extrusion_pattern.vertex.glsl index e748f5959e3..3fde3697bf2 100644 --- a/src/shaders/fill_extrusion_pattern.vertex.glsl +++ b/src/shaders/fill_extrusion_pattern.vertex.glsl @@ -14,12 +14,12 @@ uniform vec3 u_lightcolor; uniform lowp vec3 u_lightpos; uniform lowp float u_lightintensity; -attribute vec4 a_pos_normal_ed; -attribute vec2 a_centroid_pos; +in vec4 a_pos_normal_ed; +in vec2 a_centroid_pos; #ifdef PROJECTION_GLOBE_VIEW -attribute vec3 a_pos_3; // Projected position on the globe -attribute vec3 a_pos_normal_3; // Surface normal at the position +in vec3 a_pos_3; // Projected position on the globe +in vec3 a_pos_normal_3; // Surface normal at the position uniform mat4 u_inv_rot_matrix; uniform vec2 u_merc_center; @@ -29,16 +29,16 @@ uniform vec3 u_up_dir; uniform float u_height_lift; #endif -varying vec2 v_pos; -varying vec4 v_lighting; +out vec2 v_pos; +out vec4 v_lighting; #ifdef FAUX_AO uniform lowp vec2 u_ao; -varying vec3 v_ao; +out vec3 v_ao; #endif #ifdef LIGHTING_3D_MODE -varying vec3 v_normal; +out vec3 v_normal; #endif #pragma mapbox: define highp float base diff --git a/src/shaders/fill_outline.fragment.glsl b/src/shaders/fill_outline.fragment.glsl index af7c5e3c873..62625a61d9f 100644 --- a/src/shaders/fill_outline.fragment.glsl +++ b/src/shaders/fill_outline.fragment.glsl @@ -1,7 +1,7 @@ #include "_prelude_fog.fragment.glsl" #include "_prelude_lighting.glsl" -varying vec2 v_pos; +in highp vec2 v_pos; uniform float u_emissive_strength; @@ -23,10 +23,10 @@ void main() { out_color = fog_dither(fog_apply_premultiplied(out_color, v_fog_pos)); #endif - gl_FragColor = out_color * (alpha * opacity); + glFragColor = out_color * (alpha * opacity); #ifdef OVERDRAW_INSPECTOR - gl_FragColor = vec4(1.0); + glFragColor = vec4(1.0); #endif HANDLE_WIREFRAME_DEBUG; } diff --git a/src/shaders/fill_outline.vertex.glsl b/src/shaders/fill_outline.vertex.glsl index a5b27a2c4f8..f7b6b8eec04 100644 --- a/src/shaders/fill_outline.vertex.glsl +++ b/src/shaders/fill_outline.vertex.glsl @@ -1,11 +1,11 @@ #include "_prelude_fog.vertex.glsl" -attribute vec2 a_pos; +in vec2 a_pos; uniform mat4 u_matrix; uniform vec2 u_world; -varying vec2 v_pos; +out highp vec2 v_pos; #pragma mapbox: define highp vec4 outline_color #pragma mapbox: define lowp float opacity diff --git a/src/shaders/fill_outline_pattern.fragment.glsl b/src/shaders/fill_outline_pattern.fragment.glsl index e7d1134097d..769f560bc9f 100644 --- a/src/shaders/fill_outline_pattern.fragment.glsl +++ b/src/shaders/fill_outline_pattern.fragment.glsl @@ -5,8 +5,8 @@ uniform vec2 u_texsize; uniform sampler2D u_image; uniform float u_emissive_strength; -varying vec2 v_pos; -varying vec2 v_pos_world; +in highp vec2 v_pos; +in highp vec2 v_pos_world; #pragma mapbox: define lowp float opacity #pragma mapbox: define lowp vec4 pattern @@ -20,13 +20,14 @@ void main() { vec2 imagecoord = mod(v_pos, 1.0); vec2 pos = mix(pattern_tl / u_texsize, pattern_br / u_texsize, imagecoord); + vec2 lod_pos = mix(pattern_tl / u_texsize, pattern_br / u_texsize, v_pos); // find distance to outline for alpha interpolation float dist = length(v_pos_world - gl_FragCoord.xy); float alpha = 1.0 - smoothstep(0.0, 1.0, dist); - vec4 out_color = texture2D(u_image, pos); + vec4 out_color = textureLodCustom(u_image, pos, lod_pos); #ifdef LIGHTING_3D_MODE out_color = apply_lighting_with_emission_ground(out_color, u_emissive_strength); @@ -35,10 +36,10 @@ void main() { out_color = fog_dither(fog_apply_premultiplied(out_color, v_fog_pos)); #endif - gl_FragColor = out_color * (alpha * opacity); + glFragColor = out_color * (alpha * opacity); #ifdef OVERDRAW_INSPECTOR - gl_FragColor = vec4(1.0); + glFragColor = vec4(1.0); #endif HANDLE_WIREFRAME_DEBUG; diff --git a/src/shaders/fill_outline_pattern.vertex.glsl b/src/shaders/fill_outline_pattern.vertex.glsl index 3c3d7b74a57..5a2883f1fc3 100644 --- a/src/shaders/fill_outline_pattern.vertex.glsl +++ b/src/shaders/fill_outline_pattern.vertex.glsl @@ -6,10 +6,10 @@ uniform vec2 u_pixel_coord_upper; uniform vec2 u_pixel_coord_lower; uniform float u_tile_units_to_pixels; -attribute vec2 a_pos; +in vec2 a_pos; -varying vec2 v_pos; -varying vec2 v_pos_world; +out highp vec2 v_pos; +out highp vec2 v_pos_world; #pragma mapbox: define lowp float opacity #pragma mapbox: define lowp vec4 pattern diff --git a/src/shaders/fill_pattern.fragment.glsl b/src/shaders/fill_pattern.fragment.glsl index 0c80d0828ac..7611693c6b7 100644 --- a/src/shaders/fill_pattern.fragment.glsl +++ b/src/shaders/fill_pattern.fragment.glsl @@ -5,7 +5,7 @@ uniform vec2 u_texsize; uniform sampler2D u_image; -varying vec2 v_pos; +in vec2 v_pos; uniform float u_emissive_strength; @@ -21,7 +21,8 @@ void main() { vec2 imagecoord = mod(v_pos, 1.0); vec2 pos = mix(pattern_tl / u_texsize, pattern_br / u_texsize, imagecoord); - vec4 out_color = texture2D(u_image, pos); + vec2 lod_pos = mix(pattern_tl / u_texsize, pattern_br / u_texsize, v_pos); + vec4 out_color = textureLodCustom(u_image, pos, lod_pos); #ifdef LIGHTING_3D_MODE out_color = apply_lighting_with_emission_ground(out_color, u_emissive_strength); @@ -30,10 +31,10 @@ void main() { out_color = fog_dither(fog_apply_premultiplied(out_color, v_fog_pos)); #endif - gl_FragColor = out_color * opacity; + glFragColor = out_color * opacity; #ifdef OVERDRAW_INSPECTOR - gl_FragColor = vec4(1.0); + glFragColor = vec4(1.0); #endif HANDLE_WIREFRAME_DEBUG; diff --git a/src/shaders/fill_pattern.vertex.glsl b/src/shaders/fill_pattern.vertex.glsl index 3436ee9c7ec..714dfac8b14 100644 --- a/src/shaders/fill_pattern.vertex.glsl +++ b/src/shaders/fill_pattern.vertex.glsl @@ -5,9 +5,9 @@ uniform vec2 u_pixel_coord_upper; uniform vec2 u_pixel_coord_lower; uniform float u_tile_units_to_pixels; -attribute vec2 a_pos; +in vec2 a_pos; -varying vec2 v_pos; +out vec2 v_pos; #pragma mapbox: define lowp float opacity #pragma mapbox: define lowp vec4 pattern diff --git a/src/shaders/globe_raster.fragment.glsl b/src/shaders/globe_raster.fragment.glsl index 952aaad578a..eb9259b041f 100644 --- a/src/shaders/globe_raster.fragment.glsl +++ b/src/shaders/globe_raster.fragment.glsl @@ -2,7 +2,9 @@ #include "_prelude_lighting.glsl" uniform sampler2D u_image0; -varying vec2 v_pos0; +uniform float u_far_z_cutoff; + +in vec2 v_pos0; #ifndef FOG uniform highp vec3 u_frustum_tl; @@ -33,7 +35,7 @@ void main() { float antialias_factor = antialias_pixel * fwidth(norm_dist_from_center); float antialias = smoothstep(0.0, antialias_factor, norm_dist_from_center); - vec4 raster = texture2D(u_image0, v_pos0); + vec4 raster = texture(u_image0, v_pos0); #ifdef LIGHTING_3D_MODE #ifdef LIGHTING_3D_ALPHA_EMISSIVENESS raster = apply_lighting_with_emission_ground(raster, raster.a); @@ -46,7 +48,7 @@ void main() { color = vec4(raster.rgb * antialias, raster.a * antialias); #endif // !LIGHTING_3D_MODE #else // CUSTOM_ANTIALIASING - color = texture2D(u_image0, v_pos0); + color = texture(u_image0, v_pos0); #ifdef LIGHTING_3D_MODE #ifdef LIGHTING_3D_ALPHA_EMISSIVENESS color = apply_lighting_with_emission_ground(color, color.a); @@ -59,9 +61,10 @@ void main() { #ifdef FOG color = fog_dither(fog_apply_premultiplied(color, v_fog_pos)); #endif - gl_FragColor = color; + color *= 1.0 - step(u_far_z_cutoff, 1.0 / gl_FragCoord.w); + glFragColor = color; #ifdef OVERDRAW_INSPECTOR - gl_FragColor = vec4(1.0); + glFragColor = vec4(1.0); #endif HANDLE_WIREFRAME_DEBUG; } diff --git a/src/shaders/globe_raster.vertex.glsl b/src/shaders/globe_raster.vertex.glsl index 6331d850be1..b54e0c7cc68 100644 --- a/src/shaders/globe_raster.vertex.glsl +++ b/src/shaders/globe_raster.vertex.glsl @@ -11,37 +11,13 @@ uniform mat3 u_grid_matrix; uniform float u_skirt_height; #ifdef GLOBE_POLES -attribute vec3 a_globe_pos; -attribute vec2 a_uv; +in vec3 a_globe_pos; +in vec2 a_uv; #else -attribute vec2 a_pos; // .xy - grid coords, .z - 1 - skirt, 0 - grid +in vec2 a_pos; // .xy - grid coords, .z - 1 - skirt, 0 - grid #endif -varying vec2 v_pos0; - -float mercatorXfromLng(float lng) { - return (180.0 + lng) / 360.0; -} - -float mercatorYfromLat(float lat) { - return (180.0 - (RAD_TO_DEG* log(tan(QUARTER_PI + lat / 2.0 * DEG_TO_RAD)))) / 360.0; -} - -vec3 latLngToECEF(vec2 latLng) { - latLng = DEG_TO_RAD * latLng; - - float cosLat = cos(latLng[0]); - float sinLat = sin(latLng[0]); - float cosLng = cos(latLng[1]); - float sinLng = sin(latLng[1]); - - // Convert lat & lng to spherical representation. Use zoom=0 as a reference - float sx = cosLat * sinLng * GLOBE_RADIUS; - float sy = -sinLat * GLOBE_RADIUS; - float sz = cosLat * cosLng * GLOBE_RADIUS; - - return vec3(sx, sy, sz); -} +out vec2 v_pos0; void main() { #ifdef GLOBE_POLES diff --git a/src/shaders/heatmap.fragment.glsl b/src/shaders/heatmap.fragment.glsl index 5d2d215ceee..3c2c0452b0a 100644 --- a/src/shaders/heatmap.fragment.glsl +++ b/src/shaders/heatmap.fragment.glsl @@ -2,7 +2,7 @@ uniform highp float u_intensity; -varying vec2 v_extrude; +in vec2 v_extrude; #pragma mapbox: define highp float weight @@ -16,7 +16,7 @@ void main() { float d = -0.5 * 3.0 * 3.0 * dot(v_extrude, v_extrude); float val = weight * u_intensity * GAUSS_COEF * exp(d); - gl_FragColor = vec4(val, 1.0, 1.0, 1.0); + glFragColor = vec4(val, 1.0, 1.0, 1.0); #ifdef FOG // Globe uses a fixed range and heatmaps preserve @@ -26,12 +26,12 @@ void main() { // Heatmaps work differently than other layers, so we operate on the accumulated // density rather than a final color. The power is chosen so that the density // fades into the fog at a reasonable rate. - gl_FragColor.r *= pow(1.0 - fog_opacity(v_fog_pos), 2.0); + glFragColor.r *= pow(1.0 - fog_opacity(v_fog_pos), 2.0); } #endif #ifdef OVERDRAW_INSPECTOR - gl_FragColor = vec4(1.0); + glFragColor = vec4(1.0); #endif HANDLE_WIREFRAME_DEBUG; diff --git a/src/shaders/heatmap.vertex.glsl b/src/shaders/heatmap.vertex.glsl index 15b493be158..2ab30120661 100644 --- a/src/shaders/heatmap.vertex.glsl +++ b/src/shaders/heatmap.vertex.glsl @@ -6,11 +6,11 @@ uniform float u_extrude_scale; uniform float u_opacity; uniform float u_intensity; -attribute vec2 a_pos; +in vec2 a_pos; #ifdef PROJECTION_GLOBE_VIEW -attribute vec3 a_pos_3; // Projected position on the globe -attribute vec3 a_pos_normal_3; // Surface normal at the position +in vec3 a_pos_3; // Projected position on the globe +in vec3 a_pos_normal_3; // Surface normal at the position // Uniforms required for transition between globe and mercator uniform mat4 u_inv_rot_matrix; @@ -20,7 +20,7 @@ uniform float u_zoom_transition; uniform vec3 u_up_dir; #endif -varying vec2 v_extrude; +out vec2 v_extrude; #pragma mapbox: define highp float weight #pragma mapbox: define mediump float radius @@ -43,7 +43,7 @@ void main(void) { // This 'extrude' comes in ranging from [-1, -1], to [1, 1]. We'll use // it to produce the vertices of a square mesh framing the point feature // we're adding to the kernel density texture. We'll also pass it as - // a varying, so that the fragment shader can determine the distance of + // a out, so that the fragment shader can determine the distance of // each fragment from the point feature. // Before we do so, we need to scale it up sufficiently so that the // kernel falls effectively to zero at the edge of the mesh. @@ -53,7 +53,7 @@ void main(void) { // S = sqrt(-2.0 * log(ZERO / (weight * u_intensity * GAUSS_COEF))) / 3.0 float S = sqrt(-2.0 * log(ZERO / weight / u_intensity / GAUSS_COEF)) / 3.0; - // Pass the varying in units of radius + // Pass the out in units of radius v_extrude = S * unscaled_extrude; // Scale by radius and the zoom-based scale factor to produce actual diff --git a/src/shaders/heatmap_texture.fragment.glsl b/src/shaders/heatmap_texture.fragment.glsl index 5a564bbfe9d..ce2d3e05221 100644 --- a/src/shaders/heatmap_texture.fragment.glsl +++ b/src/shaders/heatmap_texture.fragment.glsl @@ -1,16 +1,16 @@ uniform sampler2D u_image; uniform sampler2D u_color_ramp; uniform float u_opacity; -varying vec2 v_pos; +in vec2 v_pos; void main() { - float t = texture2D(u_image, v_pos).r; - vec4 color = texture2D(u_color_ramp, vec2(t, 0.5)); + float t = texture(u_image, v_pos).r; + vec4 color = texture(u_color_ramp, vec2(t, 0.5)); - gl_FragColor = color * u_opacity; + glFragColor = color * u_opacity; #ifdef OVERDRAW_INSPECTOR - gl_FragColor = vec4(0.0); + glFragColor = vec4(0.0); #endif HANDLE_WIREFRAME_DEBUG; diff --git a/src/shaders/heatmap_texture.vertex.glsl b/src/shaders/heatmap_texture.vertex.glsl index 439877b6e7a..03fdf59482e 100644 --- a/src/shaders/heatmap_texture.vertex.glsl +++ b/src/shaders/heatmap_texture.vertex.glsl @@ -1,5 +1,5 @@ -attribute vec2 a_pos; -varying vec2 v_pos; +in vec2 a_pos; +out vec2 v_pos; void main() { gl_Position = vec4(a_pos, 0, 1); diff --git a/src/shaders/hillshade.fragment.glsl b/src/shaders/hillshade.fragment.glsl index dfe7e85d891..f4c18e8b80f 100644 --- a/src/shaders/hillshade.fragment.glsl +++ b/src/shaders/hillshade.fragment.glsl @@ -2,7 +2,7 @@ #include "_prelude_lighting.glsl" uniform sampler2D u_image; -varying vec2 v_pos; +in vec2 v_pos; uniform vec2 u_latrange; uniform vec2 u_light; @@ -12,7 +12,7 @@ uniform vec4 u_accent; uniform float u_emissive_strength; void main() { - vec4 pixel = texture2D(u_image, v_pos); + vec4 pixel = texture(u_image, v_pos); vec2 deriv = ((pixel.rg * 2.0) - 1.0); @@ -46,17 +46,17 @@ void main() { vec4 accent_color = (1.0 - accent) * u_accent * clamp(intensity * 2.0, 0.0, 1.0); float shade = abs(mod((aspect + azimuth) / PI + 0.5, 2.0) - 1.0); vec4 shade_color = mix(u_shadow, u_highlight, shade) * sin(scaledSlope) * clamp(intensity * 2.0, 0.0, 1.0); - gl_FragColor = accent_color * (1.0 - shade_color.a) + shade_color; + glFragColor = accent_color * (1.0 - shade_color.a) + shade_color; #ifdef LIGHTING_3D_MODE - gl_FragColor = apply_lighting_with_emission_ground(gl_FragColor, u_emissive_strength); + glFragColor = apply_lighting_with_emission_ground(glFragColor, u_emissive_strength); #endif #ifdef FOG - gl_FragColor = fog_dither(fog_apply_premultiplied(gl_FragColor, v_fog_pos)); + glFragColor = fog_dither(fog_apply_premultiplied(glFragColor, v_fog_pos)); #endif #ifdef OVERDRAW_INSPECTOR - gl_FragColor = vec4(1.0); + glFragColor = vec4(1.0); #endif HANDLE_WIREFRAME_DEBUG; diff --git a/src/shaders/hillshade.vertex.glsl b/src/shaders/hillshade.vertex.glsl index 69e8cdb2a9c..44ed6c806bf 100644 --- a/src/shaders/hillshade.vertex.glsl +++ b/src/shaders/hillshade.vertex.glsl @@ -2,10 +2,10 @@ uniform mat4 u_matrix; -attribute vec2 a_pos; -attribute vec2 a_texture_pos; +in vec2 a_pos; +in vec2 a_texture_pos; -varying vec2 v_pos; +out vec2 v_pos; void main() { gl_Position = u_matrix * vec4(a_pos, 0, 1); diff --git a/src/shaders/hillshade_prepare.fragment.glsl b/src/shaders/hillshade_prepare.fragment.glsl index 874203f403e..9ab1d905abc 100644 --- a/src/shaders/hillshade_prepare.fragment.glsl +++ b/src/shaders/hillshade_prepare.fragment.glsl @@ -1,22 +1,12 @@ -#ifdef GL_ES precision highp float; -#endif uniform sampler2D u_image; -varying vec2 v_pos; +in vec2 v_pos; uniform vec2 u_dimension; uniform float u_zoom; -uniform vec4 u_unpack; float getElevation(vec2 coord) { -#ifdef TERRAIN_DEM_FLOAT_FORMAT return texture(u_image, coord).r / 4.0; -#else - // Convert encoded elevation value to meters - vec4 data = texture2D(u_image, coord) * 255.0; - data.a = -1.0; - return dot(data, u_unpack) / 4.0; -#endif } void main() { @@ -66,7 +56,7 @@ void main() { (f + g + g + h) - (a + b + b + c) ) / pow(2.0, exaggeration + (19.2562 - u_zoom)); - gl_FragColor = clamp(vec4( + glFragColor = clamp(vec4( deriv.x / 2.0 + 0.5, deriv.y / 2.0 + 0.5, 1.0, diff --git a/src/shaders/hillshade_prepare.vertex.glsl b/src/shaders/hillshade_prepare.vertex.glsl index 582397d6df0..4133d46a575 100644 --- a/src/shaders/hillshade_prepare.vertex.glsl +++ b/src/shaders/hillshade_prepare.vertex.glsl @@ -1,10 +1,10 @@ uniform mat4 u_matrix; uniform vec2 u_dimension; -attribute vec2 a_pos; -attribute vec2 a_texture_pos; +in vec2 a_pos; +in vec2 a_texture_pos; -varying vec2 v_pos; +out vec2 v_pos; void main() { gl_Position = u_matrix * vec4(a_pos, 0, 1); diff --git a/src/shaders/line.fragment.glsl b/src/shaders/line.fragment.glsl index 7b4dba196af..6b7b6fbae96 100644 --- a/src/shaders/line.fragment.glsl +++ b/src/shaders/line.fragment.glsl @@ -5,14 +5,14 @@ uniform lowp float u_device_pixel_ratio; uniform float u_alpha_discard_threshold; uniform highp vec2 u_trim_offset; -varying vec2 v_width2; -varying vec2 v_normal; -varying float v_gamma_scale; -varying highp vec4 v_uv; +in vec2 v_width2; +in vec2 v_normal; +in float v_gamma_scale; +in highp vec4 v_uv; #ifdef RENDER_LINE_DASH uniform sampler2D u_dash_image; -varying vec2 v_tex; +in vec2 v_tex; #endif #ifdef RENDER_LINE_GRADIENT @@ -56,7 +56,7 @@ void main() { float blur2 = (blur + 1.0 / u_device_pixel_ratio) * v_gamma_scale; float alpha = clamp(min(dist - (v_width2.t - blur2), v_width2.s - dist) / blur2, 0.0, 1.0); #ifdef RENDER_LINE_DASH - float sdfdist = texture2D(u_dash_image, v_tex).a; + float sdfdist = texture(u_dash_image, v_tex).r; float sdfgamma = 1.0 / (2.0 * u_device_pixel_ratio) / dash.z; alpha *= linearstep(0.5 - sdfgamma / floorwidth, 0.5 + sdfgamma / floorwidth, sdfdist); #endif @@ -64,7 +64,7 @@ void main() { highp vec4 out_color; #ifdef RENDER_LINE_GRADIENT // For gradient lines, v_uv.xy are the coord specify where the texture will be simpled. - out_color = texture2D(u_gradient_image, v_uv.xy); + out_color = texture(u_gradient_image, v_uv.xy); #else out_color = color; #endif @@ -133,10 +133,10 @@ void main() { out_color = applyCutout(out_color); #endif - gl_FragColor = out_color; + glFragColor = out_color; #ifdef OVERDRAW_INSPECTOR - gl_FragColor = vec4(1.0); + glFragColor = vec4(1.0); #endif HANDLE_WIREFRAME_DEBUG; diff --git a/src/shaders/line.vertex.glsl b/src/shaders/line.vertex.glsl index 8fab8328b10..2abc326aee1 100644 --- a/src/shaders/line.vertex.glsl +++ b/src/shaders/line.vertex.glsl @@ -8,17 +8,17 @@ // #define scale 63.0 #define EXTRUDE_SCALE 0.015873016 -attribute vec2 a_pos_normal; -attribute vec4 a_data; +in vec2 a_pos_normal; +in vec4 a_data; // Includes in order: a_uv_x, a_split_index, a_clip_start, a_clip_end // to reduce attribute count on older devices. // Only line-gradient and line-trim-offset will requires a_packed info. #if defined(RENDER_LINE_GRADIENT) || defined(RENDER_LINE_TRIM_OFFSET) -attribute highp vec4 a_packed; +in highp vec4 a_packed; #endif #ifdef RENDER_LINE_DASH -attribute float a_linesofar; +in float a_linesofar; #endif uniform mat4 u_matrix; @@ -26,15 +26,15 @@ uniform mat2 u_pixels_to_tile_units; uniform vec2 u_units_to_pixels; uniform lowp float u_device_pixel_ratio; -varying vec2 v_normal; -varying vec2 v_width2; -varying float v_gamma_scale; -varying highp vec4 v_uv; +out vec2 v_normal; +out vec2 v_width2; +out float v_gamma_scale; +out highp vec4 v_uv; #ifdef RENDER_LINE_DASH uniform vec2 u_texsize; uniform float u_tile_units_to_pixels; -varying vec2 v_tex; +out vec2 v_tex; #endif #ifdef RENDER_LINE_GRADIENT diff --git a/src/shaders/line_pattern.fragment.glsl b/src/shaders/line_pattern.fragment.glsl index 5f9e0ff4bab..9b1de6ad2e1 100644 --- a/src/shaders/line_pattern.fragment.glsl +++ b/src/shaders/line_pattern.fragment.glsl @@ -3,15 +3,23 @@ uniform lowp float u_device_pixel_ratio; uniform vec2 u_texsize; -uniform float u_tile_units_to_pixels; +uniform mediump float u_tile_units_to_pixels; +uniform highp vec2 u_trim_offset; uniform sampler2D u_image; -varying vec2 v_normal; -varying vec2 v_width2; -varying float v_linesofar; -varying float v_gamma_scale; -varying float v_width; +in vec2 v_normal; +in vec2 v_width2; +in float v_linesofar; +in float v_gamma_scale; +in float v_width; +#ifdef RENDER_LINE_TRIM_OFFSET +in highp vec4 v_uv; +#endif + +#ifdef LINE_JOIN_NONE +in vec2 v_pattern_data; // [pos_in_segment, segment_length]; +#endif #pragma mapbox: define lowp vec4 pattern #pragma mapbox: define lowp float pixel_ratio @@ -30,27 +38,69 @@ void main() { vec2 display_size = (pattern_br - pattern_tl) / pixel_ratio; - vec2 pattern_size = vec2(display_size.x / u_tile_units_to_pixels, display_size.y); - float aspect = display_size.y / v_width; + float pattern_size_x = display_size.x / (u_tile_units_to_pixels * aspect); // Calculate the distance of the pixel from the line in pixels. - float dist = length(v_normal) * v_width2.s; + float dist = length(v_normal) * v_width2.x; // Calculate the antialiasing fade factor. This is either when fading in - // the line in case of an offset line (v_width2.t) or when fading out - // (v_width2.s) + // the line in case of an offset line (v_width2.y) or when fading out + // (v_width2.x) float blur2 = (blur + 1.0 / u_device_pixel_ratio) * v_gamma_scale; - float alpha = clamp(min(dist - (v_width2.t - blur2), v_width2.s - dist) / blur2, 0.0, 1.0); + float alpha = clamp(min(dist - (v_width2.y - blur2), v_width2.x - dist) / blur2, 0.0, 1.0); - float x = mod(v_linesofar / pattern_size.x * aspect, 1.0); + float pattern_x = v_linesofar / pattern_size_x; + float x = fract(pattern_x); float y = 0.5 * v_normal.y + 0.5; vec2 texel_size = 1.0 / u_texsize; - vec2 pos = mix(pattern_tl * texel_size - texel_size, pattern_br * texel_size + texel_size, vec2(x, y)); - vec4 color = texture2D(u_image, pos); + vec2 tl = pattern_tl * texel_size - texel_size; + vec2 br = pattern_br * texel_size + texel_size; + vec2 pos = mix(tl, br, vec2(x, y)); + float lod_pos_x = mix(tl.x, br.x, pattern_x); + vec4 color = textureLodCustom(u_image, pos, vec2(lod_pos_x, pos.y)); + +#ifdef RENDER_LINE_TRIM_OFFSET + // v_uv[2] and v_uv[3] are specifying the original clip range that the vertex is located in. + highp float start = v_uv[2]; + highp float end = v_uv[3]; + highp float trim_start = u_trim_offset[0]; + highp float trim_end = u_trim_offset[1]; + // v_uv.x is the relative prorgress based on each clip. Calculate the absolute progress based on + // the whole line by combining the clip start and end value. + highp float line_progress = (start + (v_uv.x) * (end - start)); + // Mark the pixel to be transparent when: + // 1. trim_offset range is valid + // 2. line_progress is within trim_offset range + + // Nested conditionals fixes the issue + // https://github.com/mapbox/mapbox-gl-js/issues/12013 + if (trim_end > trim_start) { + if (line_progress <= trim_end && line_progress >= trim_start) { + color = vec4(0, 0, 0, 0); + } + } +#endif + +#ifdef LINE_JOIN_NONE + // v_pattern_data = { x = pos_in_segment, y = segment_length } + // v_linesofar and v_pattern_data.x is offset in vertex shader based on segment overlap (v_pattern_data.x can be + // negative). v_pattern_data.y is not modified because we can't access overlap info for other end of the segment. + // All units are tile units. + // Distance from segment start point to start of first pattern instance + float segment_phase = pattern_size_x - mod((v_linesofar - v_pattern_data.x), pattern_size_x); + // Step is used to check if we can fit an extra pattern cycle when considering the segment overlap at the corner + float visible_start = segment_phase - step(pattern_size_x * 0.5, segment_phase) * pattern_size_x; + float visible_end = floor((v_pattern_data.y - segment_phase) / pattern_size_x) * pattern_size_x + segment_phase; + visible_end += step(pattern_size_x * 0.5, v_pattern_data.y - visible_end) * pattern_size_x; + + if (v_pattern_data.x < visible_start || v_pattern_data.x >= visible_end) { + color = vec4(0.0); + } +#endif #ifdef LIGHTING_3D_MODE color = apply_lighting_ground(color); @@ -65,10 +115,10 @@ void main() { color = applyCutout(color); #endif - gl_FragColor = color; + glFragColor = color; #ifdef OVERDRAW_INSPECTOR - gl_FragColor = vec4(1.0); + glFragColor = vec4(1.0); #endif HANDLE_WIREFRAME_DEBUG; diff --git a/src/shaders/line_pattern.vertex.glsl b/src/shaders/line_pattern.vertex.glsl index c9147cc66d9..583fd1c43a1 100644 --- a/src/shaders/line_pattern.vertex.glsl +++ b/src/shaders/line_pattern.vertex.glsl @@ -8,20 +8,35 @@ // #define scale 63.0 #define scale 0.015873016 -attribute vec2 a_pos_normal; -attribute vec4 a_data; -attribute float a_linesofar; +in vec2 a_pos_normal; +in vec4 a_data; +// Includes in order: a_uv_x, a_split_index, a_clip_start, a_clip_end +// to reduce attribute count on older devices. +// Only line-trim-offset will requires a_packed info. +#ifdef RENDER_LINE_TRIM_OFFSET +in highp vec4 a_packed; +#endif +in float a_linesofar; + +#ifdef LINE_JOIN_NONE +in vec2 a_pattern_data; // [position_in_segment & offset_sign, segment_length]; +out vec2 v_pattern_data; // [position_in_segment, segment_length] +#endif uniform mat4 u_matrix; +uniform mediump float u_tile_units_to_pixels; uniform vec2 u_units_to_pixels; uniform mat2 u_pixels_to_tile_units; uniform lowp float u_device_pixel_ratio; -varying vec2 v_normal; -varying vec2 v_width2; -varying float v_linesofar; -varying float v_gamma_scale; -varying float v_width; +out vec2 v_normal; +out vec2 v_width2; +out float v_linesofar; +out float v_gamma_scale; +out float v_width; +#ifdef RENDER_LINE_TRIM_OFFSET +out highp vec4 v_uv; +#endif #pragma mapbox: define lowp float blur #pragma mapbox: define lowp float opacity @@ -44,9 +59,10 @@ void main() { // the distance over which the line edge fades out. // Retina devices need a smaller distance to avoid aliasing. - float ANTIALIASING = 1.0 / u_device_pixel_ratio / 2.0; + float ANTIALIASING = 0.5 / u_device_pixel_ratio; - vec2 a_extrude = a_data.xy - 128.0; + // Scale the extrusion vector down to 1<=length<=2 scale + vec2 extrude = (a_data.xy - 128.0) * scale; float a_direction = mod(a_data.z, 4.0) - 1.0; vec2 pos = floor(a_pos_normal * 0.5); @@ -60,16 +76,13 @@ void main() { // these transformations used to be applied in the JS and native code bases. // moved them into the shader for clarity and simplicity. - gapwidth = gapwidth / 2.0; - float halfwidth = width / 2.0; - offset = -1.0 * offset; + gapwidth = gapwidth * 0.5; float inset = gapwidth + (gapwidth > 0.0 ? ANTIALIASING : 0.0); - float outset = gapwidth + halfwidth * (gapwidth > 0.0 ? 2.0 : 1.0) + (halfwidth == 0.0 ? 0.0 : ANTIALIASING); + float outset = gapwidth + width * (gapwidth > 0.0 ? 1.0 : 0.5) + (width == 0.0 ? 0.0 : ANTIALIASING); - // Scale the extrusion vector down to a normal and then up by the line width - // of this vertex. - mediump vec2 dist = outset * a_extrude * scale; + // Scale the extrusion vector up by the line width of this vertex + mediump vec2 dist = outset * extrude; // Calculate the offset when drawing a line that is to the side of the actual line. // We do this by creating a vector that points towards the extrude, but rotate @@ -77,7 +90,7 @@ void main() { // extrude vector points in another direction. mediump float u = 0.5 * a_direction; mediump float t = 1.0 - abs(u); - mediump vec2 offset2 = offset * a_extrude * scale * normal.y * mat2(t, -u, u, t); + mediump vec2 offset2 = -offset * extrude * normal.y * mat2(t, -u, u, t); vec4 projected_extrude = u_matrix * vec4(dist * u_pixels_to_tile_units, 0.0, 0.0); gl_Position = u_matrix * vec4(pos + offset2 * u_pixels_to_tile_units, 0.0, 1.0) + projected_extrude; @@ -90,10 +103,28 @@ void main() { #else v_gamma_scale = 1.0; #endif + +#ifdef RENDER_LINE_TRIM_OFFSET + float a_uv_x = a_packed[0]; + highp float a_clip_start = a_packed[2]; + highp float a_clip_end = a_packed[3]; + v_uv = vec4(a_uv_x, 0.0, a_clip_start, a_clip_end); +#endif + v_linesofar = a_linesofar; v_width2 = vec2(outset, inset); v_width = floorwidth; +#ifdef LINE_JOIN_NONE + // Needs to consider antialiasing width extension to get accurate pattern aspect ratio + v_width += ANTIALIASING; + // Offset caused by vertices extended forward or backward from line point + float offset_sign = (fract(a_pattern_data.x) - 0.5) * 4.0; + float line_progress_offset = offset_sign * v_width * 0.5 / u_tile_units_to_pixels; + v_linesofar += line_progress_offset; + v_pattern_data = vec2(a_pattern_data.x + line_progress_offset, a_pattern_data.y); +#endif + #ifdef FOG v_fog_pos = fog_position(pos); #endif diff --git a/src/shaders/raster.fragment.glsl b/src/shaders/raster.fragment.glsl index 91c2bf49289..1eb68636602 100644 --- a/src/shaders/raster.fragment.glsl +++ b/src/shaders/raster.fragment.glsl @@ -1,12 +1,18 @@ #include "_prelude_fog.fragment.glsl" #include "_prelude_lighting.glsl" +#include "_prelude_raster_array.glsl" uniform float u_fade_t; uniform float u_opacity; -uniform sampler2D u_image0; -uniform sampler2D u_image1; -varying vec2 v_pos0; -varying vec2 v_pos1; +uniform highp float u_raster_elevation; +uniform highp float u_zoom_transition; + +in vec2 v_pos0; +in vec2 v_pos1; +in float v_depth; +#ifdef PROJECTION_GLOBE_VIEW +in float v_split_fade; +#endif uniform float u_brightness_low; uniform float u_brightness_high; @@ -15,51 +21,73 @@ uniform float u_saturation_factor; uniform float u_contrast_factor; uniform vec3 u_spin_weights; +uniform float u_emissive_strength; + +#ifndef RASTER_ARRAY +// Since samplers cannot be used as function parameters, they must be hard-coded. These +// are therefore instead moved to the raster_array prelude when raster arrays are active. +uniform sampler2D u_image0; +uniform sampler2D u_image1; +#endif + #ifdef RASTER_COLOR uniform sampler2D u_color_ramp; -uniform highp vec2 u_colorization_scale; uniform highp vec4 u_colorization_mix; - -highp vec4 colormap (highp float value) { - highp float scaled_value = value * u_colorization_scale.y + u_colorization_scale.x; - highp vec2 coords = vec2(scaled_value, 0.5); - return texture2D(u_color_ramp, coords); -} +uniform highp float u_colorization_offset; +uniform vec2 u_texture_res; #endif + void main() { + vec4 color0, color1, color; + vec2 value; - // read and cross-fade colors from the main and parent tiles - vec4 color0 = texture2D(u_image0, v_pos0); - vec4 color1 = texture2D(u_image1, v_pos1); +#ifdef RASTER_COLOR - vec4 color; +#ifdef RASTER_ARRAY + // For raster-arrays, we take extra care to decode values strictly correctly, + // reimplementing linear interpolation in-shader, if necessary. +#ifdef RASTER_ARRAY_LINEAR + value = mix( + raTexture2D_image0_linear(v_pos0, u_texture_res, u_colorization_mix, u_colorization_offset), + raTexture2D_image1_linear(v_pos1, u_texture_res, u_colorization_mix, u_colorization_offset), + u_fade_t + ); +#else + value = mix( + raTexture2D_image0_nearest(v_pos0, u_texture_res, u_colorization_mix, u_colorization_offset), + raTexture2D_image1_nearest(v_pos1, u_texture_res, u_colorization_mix, u_colorization_offset), + u_fade_t + ); +#endif + // Divide the scalar value by "alpha" to smoothly fade to no data + if (value.y > 0.0) value.x /= value.y; +#else + color = mix(texture(u_image0, v_pos0), texture(u_image1, v_pos1), u_fade_t); + value = vec2(u_colorization_offset + dot(color.rgb, u_colorization_mix.rgb), color.a); +#endif -#ifdef RASTER_COLOR - // In the case of raster colorization, interpolate the raster value first, - // then sample the color map. Otherwise we interpolate two tabulated colors - // and end up with a color *not* on the color map. - highp vec4 fadedColor = mix(color0, color1, u_fade_t); - color = colormap(dot(vec4(fadedColor.rgb, 1), u_colorization_mix)); + color = texture(u_color_ramp, vec2(value.x, 0.5)); // Apply input alpha on top of color ramp alpha - color.a *= fadedColor.a; + if (color.a > 0.0) color.rgb /= color.a; + + color.a *= value.y; - if (color.a > 0.0) { - color.rgb /= color.a; - } #else - if (color0.a > 0.0) { - color0.rgb /= color0.a; - } - if (color1.a > 0.0) { - color1.rgb /= color1.a; - } + // read and cross-fade colors from the main and parent tiles + color0 = texture(u_image0, v_pos0); + color1 = texture(u_image1, v_pos1); + + if (color0.a > 0.0) color0.rgb /= color0.a; + if (color1.a > 0.0) color1.rgb /= color1.a; color = mix(color0, color1, u_fade_t); #endif - color.a *= u_opacity; +#ifdef GLOBE_POLES + color.a *= 1.0 - smoothstep(0.0, 0.05, u_zoom_transition); +#endif vec3 rgb = color.rgb; // spin @@ -82,16 +110,26 @@ void main() { vec3 out_color = mix(u_high_vec, u_low_vec, rgb); #ifdef LIGHTING_3D_MODE - out_color = apply_lighting_ground(out_color); + out_color = apply_lighting_with_emission_ground(vec4(out_color, 1.0), u_emissive_strength).rgb; #endif #ifdef FOG - out_color = fog_dither(fog_apply(out_color, v_fog_pos)); + highp float fog_limit_high_meters = 1000000.0; + highp float fog_limit_low_meters = 600000.0; + float fog_limit = 1.0 - smoothstep(fog_limit_low_meters, fog_limit_high_meters, u_raster_elevation); + out_color = fog_dither(fog_apply(out_color, v_fog_pos, fog_limit)); #endif - gl_FragColor = vec4(out_color * color.a, color.a); + glFragColor = vec4(out_color * color.a, color.a); +#ifdef PROJECTION_GLOBE_VIEW + glFragColor *= mix(1.0, 1.0 - smoothstep(0.0, 0.05, u_zoom_transition), smoothstep(0.8, 0.9, v_split_fade)); +#endif + +#ifdef RENDER_CUTOFF + glFragColor = glFragColor * cutoff_opacity(u_cutoff_params, v_depth); +#endif #ifdef OVERDRAW_INSPECTOR - gl_FragColor = vec4(1.0); + glFragColor = vec4(1.0); #endif HANDLE_WIREFRAME_DEBUG; diff --git a/src/shaders/raster.vertex.glsl b/src/shaders/raster.vertex.glsl index b2616205740..515c183bea1 100644 --- a/src/shaders/raster.vertex.glsl +++ b/src/shaders/raster.vertex.glsl @@ -3,43 +3,108 @@ uniform mat4 u_matrix; uniform mat4 u_normalize_matrix; uniform mat4 u_globe_matrix; +uniform mat4 u_merc_matrix; +uniform mat3 u_grid_matrix; uniform vec2 u_tl_parent; uniform float u_scale_parent; uniform vec2 u_perspective_transform; +uniform vec2 u_texture_offset; +uniform float u_raster_elevation; +uniform float u_zoom_transition; +uniform vec2 u_merc_center; -#ifdef PROJECTION_GLOBE_VIEW -attribute vec3 a_globe_pos; -attribute vec2 a_uv; +#define GLOBE_UPSCALE GLOBE_RADIUS / 6371008.8 + +#ifdef GLOBE_POLES +in vec3 a_globe_pos; +in vec2 a_uv; #else -attribute vec2 a_pos; -attribute vec2 a_texture_pos; +in vec2 a_pos; +in vec2 a_texture_pos; #endif -varying vec2 v_pos0; -varying vec2 v_pos1; +out vec2 v_pos0; +out vec2 v_pos1; +out float v_depth; +#ifdef PROJECTION_GLOBE_VIEW +out float v_split_fade; +#endif void main() { vec2 uv; -#ifdef PROJECTION_GLOBE_VIEW - gl_Position = u_matrix * u_globe_matrix * vec4(a_globe_pos, 1.0); +#ifdef GLOBE_POLES + vec3 globe_pos = a_globe_pos; + globe_pos += normalize(globe_pos) * u_raster_elevation * GLOBE_UPSCALE; + gl_Position = u_matrix * u_globe_matrix * vec4(globe_pos , 1.0); uv = a_uv; #ifdef FOG v_fog_pos = fog_position((u_normalize_matrix * vec4(a_globe_pos, 1.0)).xyz); -#endif -#else +#endif // FOG +#else // else GLOBE_POLES float w = 1.0 + dot(a_texture_pos, u_perspective_transform); - gl_Position = u_matrix * vec4(a_pos * w, 0, w); -#ifdef FOG - v_fog_pos = fog_position(a_pos); -#endif // We are using Int16 for texture position coordinates to give us enough precision for // fractional coordinates. We use 8192 to scale the texture coordinates in the buffer // as an arbitrarily high number to preserve adequate precision when rendering. // This is also the same value as the EXTENT we are using for our tile buffer pos coordinates, // so math for modifying either is consistent. uv = a_texture_pos / 8192.0; -#endif +#ifdef PROJECTION_GLOBE_VIEW + vec3 decomposed_pos_and_skirt = decomposeToPosAndSkirt(a_pos); + vec3 latLng = u_grid_matrix * vec3(decomposed_pos_and_skirt.xy, 1.0); + vec3 globe_pos = latLngToECEF(latLng.xy); + globe_pos += normalize(globe_pos) * u_raster_elevation * GLOBE_UPSCALE; + vec4 globe_world_pos = u_globe_matrix * vec4(globe_pos, 1.0); + vec4 merc_world_pos = vec4(0.0); + float mercatorY = mercatorYfromLat(latLng[0]); + float mercatorX = mercatorXfromLng(latLng[1]); + v_split_fade = 0.0; + if (u_zoom_transition > 0.0) { + vec2 merc_pos = vec2(mercatorX, mercatorY); + merc_world_pos = vec4(merc_pos, u_raster_elevation, 1.0); + merc_world_pos.xy -= u_merc_center; + merc_world_pos.x = wrap(merc_world_pos.x, -0.5, 0.5); + merc_world_pos = u_merc_matrix * merc_world_pos; + + float opposite_merc_center = mod(u_merc_center.x + 0.5, 1.0); + float dist_from_poles = (abs(mercatorY - 0.5) * 2.0); + float range = 0.1; + v_split_fade = abs(opposite_merc_center - mercatorX); + v_split_fade = clamp(1.0 - v_split_fade, 0.0, 1.0); + v_split_fade = max(smoothstep(1.0 - range, 1.0, dist_from_poles), max(smoothstep(1.0 - range, 1.0, v_split_fade), smoothstep(1.0 - range, 1.0, 1.0 - v_split_fade))); + } + + float tiles = u_grid_matrix[0][2]; + if (tiles > 0.0) { + float idx = u_grid_matrix[1][2]; + float idy = u_grid_matrix[2][2]; + float uvY = mercatorY * tiles - idy; + float uvX = mercatorX * tiles - idx; + uv = vec2(uvX, uvY); + } + + vec4 interpolated_pos = vec4(mix(globe_world_pos.xyz, merc_world_pos.xyz, u_zoom_transition) * w, w); + + gl_Position = u_matrix * interpolated_pos; +#ifdef FOG + v_fog_pos = fog_position((u_normalize_matrix * vec4(globe_pos, 1.0)).xyz); +#endif // FOG +#else // else PROJECTION_GLOBE_VIEW + gl_Position = u_matrix * vec4(a_pos * w, u_raster_elevation * w, w); +#ifdef FOG + v_fog_pos = fog_position(a_pos); +#endif // FOG +#endif // else PROJECTION_GLOBE_VIEW +#endif // else GLOBE_POLES v_pos0 = uv; v_pos1 = (v_pos0 * u_scale_parent) + u_tl_parent; + + // Correct the texture coord for a buffer, for example if tiles have a 1px buffer and + // are therefore 258 x 258 or 514 x 514. + v_pos0 = u_texture_offset.x + u_texture_offset.y * v_pos0; + v_pos1 = u_texture_offset.x + u_texture_offset.y * v_pos1; + +#ifdef RENDER_CUTOFF + v_depth = gl_Position.z; +#endif } diff --git a/src/shaders/raster_particle.fragment.glsl b/src/shaders/raster_particle.fragment.glsl new file mode 100644 index 00000000000..810f6b52d53 --- /dev/null +++ b/src/shaders/raster_particle.fragment.glsl @@ -0,0 +1,45 @@ +#include "_prelude_fog.fragment.glsl" +#include "_prelude_lighting.glsl" + +uniform float u_fade_t; +uniform float u_opacity; +uniform highp float u_raster_elevation; + +in vec2 v_pos0; +in vec2 v_pos1; + +uniform sampler2D u_image0; +uniform sampler2D u_image1; + +void main() { + vec4 color0, color1, color; + + // read and cross-fade colors from the main and parent tiles + color0 = texture(u_image0, v_pos0); + color1 = texture(u_image1, v_pos1); + + if (color0.a > 0.0) color0.rgb /= color0.a; + if (color1.a > 0.0) color1.rgb /= color1.a; + color = mix(color0, color1, u_fade_t); + color.a *= u_opacity; + + vec3 out_color = color.rgb; + +#ifdef LIGHTING_3D_MODE + out_color = apply_lighting_with_emission_ground(vec4(out_color, 1.0), 0.0).rgb; +#endif +#ifdef FOG + highp float fog_limit_high_meters = 1000000.0; + highp float fog_limit_low_meters = 600000.0; + float fog_limit = 1.0 - smoothstep(fog_limit_low_meters, fog_limit_high_meters, u_raster_elevation); + out_color = fog_dither(fog_apply(out_color, v_fog_pos, fog_limit)); +#endif + + glFragColor = vec4(out_color * color.a, color.a); + +#ifdef OVERDRAW_INSPECTOR + glFragColor = vec4(1.0); +#endif + + HANDLE_WIREFRAME_DEBUG; +} diff --git a/src/shaders/raster_particle.vertex.glsl b/src/shaders/raster_particle.vertex.glsl new file mode 100644 index 00000000000..84e0bd122e8 --- /dev/null +++ b/src/shaders/raster_particle.vertex.glsl @@ -0,0 +1,73 @@ +#include "_prelude_fog.vertex.glsl" + +uniform mat4 u_matrix; +uniform mat4 u_normalize_matrix; +uniform mat4 u_globe_matrix; +uniform mat4 u_merc_matrix; +uniform mat3 u_grid_matrix; +uniform vec2 u_tl_parent; +uniform float u_scale_parent; +uniform float u_raster_elevation; +uniform float u_zoom_transition; +uniform vec2 u_merc_center; + +#define GLOBE_UPSCALE GLOBE_RADIUS / 6371008.8 + +in vec2 a_pos; +in vec2 a_texture_pos; + +out vec2 v_pos0; +out vec2 v_pos1; + +void main() { + float w = 1.0; + vec2 uv; +#ifdef PROJECTION_GLOBE_VIEW + vec3 decomposed_pos_and_skirt = decomposeToPosAndSkirt(a_pos); + vec3 latLng = u_grid_matrix * vec3(decomposed_pos_and_skirt.xy, 1.0); + float mercatorY = mercatorYfromLat(latLng[0]); + float mercatorX = mercatorXfromLng(latLng[1]); + + // The 3rd row of u_grid_matrix is only used as a spare space to + // pass the following 3 uniforms to avoid explicitly introducing new ones. + float tiles = u_grid_matrix[0][2]; + float idx = u_grid_matrix[1][2]; + float idy = u_grid_matrix[2][2]; + float uvX = mercatorX * tiles - idx; + float uvY = mercatorY * tiles - idy; + uv = vec2(uvX, uvY); + + vec3 globe_pos = latLngToECEF(latLng.xy); + globe_pos += normalize(globe_pos) * u_raster_elevation * GLOBE_UPSCALE; + vec4 globe_world_pos = u_globe_matrix * vec4(globe_pos, 1.0); + vec4 merc_world_pos = vec4(0.0); + if (u_zoom_transition > 0.0) { + vec2 merc_pos = vec2(mercatorX, mercatorY); + merc_world_pos = vec4(merc_pos, u_raster_elevation, 1.0); + merc_world_pos.xy -= u_merc_center; + merc_world_pos.x = wrap(merc_world_pos.x, -0.5, 0.5); + merc_world_pos = u_merc_matrix * merc_world_pos; + } + + vec4 interpolated_pos = vec4(mix(globe_world_pos.xyz, merc_world_pos.xyz, u_zoom_transition) * w, w); + + gl_Position = u_matrix * interpolated_pos; +#ifdef FOG + v_fog_pos = fog_position((u_normalize_matrix * vec4(globe_pos, 1.0)).xyz); +#endif // FOG +#else // else PROJECTION_GLOBE_VIEW + // We are using Int16 for texture position coordinates to give us enough precision for + // fractional coordinates. We use 8192 to scale the texture coordinates in the buffer + // as an arbitrarily high number to preserve adequate precision when rendering. + // This is also the same value as the EXTENT we are using for our tile buffer pos coordinates, + // so math for modifying either is consistent. + uv = a_texture_pos / 8192.0; + gl_Position = u_matrix * vec4(a_pos * w, u_raster_elevation * w, w); +#ifdef FOG + v_fog_pos = fog_position(a_pos); +#endif // FOG +#endif // endif PROJECTION_GLOBE_VIEW + + v_pos0 = uv; + v_pos1 = (v_pos0 * u_scale_parent) + u_tl_parent; +} diff --git a/src/shaders/raster_particle_draw.fragment.glsl b/src/shaders/raster_particle_draw.fragment.glsl new file mode 100644 index 00000000000..4a612d5cd9b --- /dev/null +++ b/src/shaders/raster_particle_draw.fragment.glsl @@ -0,0 +1,9 @@ +precision highp float; + +uniform sampler2D u_color_ramp; + +in float v_particle_speed; + +void main() { + glFragColor = texture(u_color_ramp, vec2(v_particle_speed, 0.5)); +} diff --git a/src/shaders/raster_particle_draw.vertex.glsl b/src/shaders/raster_particle_draw.vertex.glsl new file mode 100644 index 00000000000..96ce322c5d7 --- /dev/null +++ b/src/shaders/raster_particle_draw.vertex.glsl @@ -0,0 +1,28 @@ +#include "_prelude_raster_particle.glsl" + +in float a_index; + +uniform sampler2D u_particle_texture; +uniform float u_particle_texture_side_len; +uniform vec2 u_tile_offset; + +out float v_particle_speed; + +void main() { + ivec2 pixel_coord = ivec2( + mod(a_index, u_particle_texture_side_len), + a_index / u_particle_texture_side_len); + vec4 pixel = texelFetch(u_particle_texture, pixel_coord, 0); + vec2 pos = decode_pos(pixel) + u_tile_offset; + + vec2 tex_coord = fract(pos); + vec2 velocity = lookup_velocity(tex_coord); + if (velocity == INVALID_VELOCITY) { + gl_Position = AWAY; + v_particle_speed = 0.0; + } else { + gl_Position = vec4(2.0 * pos - vec2(1.0), 0.0, 1.0); + v_particle_speed = length(velocity); + } + gl_PointSize = 1.0; +} diff --git a/src/shaders/raster_particle_texture.fragment.glsl b/src/shaders/raster_particle_texture.fragment.glsl new file mode 100644 index 00000000000..8ad3ae35650 --- /dev/null +++ b/src/shaders/raster_particle_texture.fragment.glsl @@ -0,0 +1,10 @@ +uniform sampler2D u_texture; +uniform float u_opacity; + +in vec2 v_tex_pos; + +void main() { + vec4 color = texture(u_texture, v_tex_pos); + // a hack to guarantee opacity fade out even with a value close to 1.0 + glFragColor = vec4(floor(255.0 * color * u_opacity) / 255.0); +} diff --git a/src/shaders/raster_particle_texture.vertex.glsl b/src/shaders/raster_particle_texture.vertex.glsl new file mode 100644 index 00000000000..957b43986ac --- /dev/null +++ b/src/shaders/raster_particle_texture.vertex.glsl @@ -0,0 +1,9 @@ +in vec2 a_pos; + +out vec2 v_tex_pos; + +void main() { + vec2 uv = 0.5 * a_pos + vec2(0.5); + v_tex_pos = uv; + gl_Position = vec4(a_pos, 0.0, 1.0); +} diff --git a/src/shaders/raster_particle_update.fragment.glsl b/src/shaders/raster_particle_update.fragment.glsl new file mode 100644 index 00000000000..7dc2a6babfc --- /dev/null +++ b/src/shaders/raster_particle_update.fragment.glsl @@ -0,0 +1,44 @@ +#include "_prelude_raster_particle.glsl" + +uniform sampler2D u_particle_texture; +uniform float u_particle_texture_side_len; +uniform float u_speed_factor; +uniform float u_reset_rate; +uniform float u_rand_seed; + +in vec2 v_tex_coord; + +// pseudo-random generator +const vec3 rand_constants = vec3(12.9898, 78.233, 4375.85453); +float rand(const vec2 co) { + float t = dot(rand_constants.xy, co); + return fract(sin(t) * (rand_constants.z + t)); +} + +void main() { + ivec2 pixel_coord = ivec2(v_tex_coord * u_particle_texture_side_len); + vec4 pixel = texelFetch(u_particle_texture, pixel_coord, 0); + vec2 pos = decode_pos(pixel); + vec2 velocity = lookup_velocity(clamp(pos, 0.0, 1.0)); + vec2 dp; +#ifdef DATA_FORMAT_UINT32 + dp = vec2(0); +#else + dp = velocity == INVALID_VELOCITY ? vec2(0) : velocity * u_speed_factor; +#endif + pos = pos + dp; + + vec2 seed = (pos + v_tex_coord) * u_rand_seed; + vec2 random_pos = vec2(rand(seed + 1.3), rand(seed + 2.1)); + float speed = velocity == INVALID_VELOCITY ? 0.0 : length(velocity); + float reset_rate_bump = speed * u_reset_rate; + vec2 particle_pos_min = -u_particle_pos_offset; + vec2 particle_pos_max = vec2(1.0) + u_particle_pos_offset; + // drop rate 0: (min pos) < x < (max pos), else drop rate 1 + vec2 pos_drop_rate = vec2(1.0) - step(particle_pos_min, pos) + step(particle_pos_max, pos); + float drop_rate = max(u_reset_rate + reset_rate_bump, length(pos_drop_rate)); + float drop = step(1.0 - drop_rate, rand(seed)); + vec2 next_pos = mix(pos, random_pos, drop); + + glFragColor = encode_pos(next_pos); +} diff --git a/src/shaders/raster_particle_update.vertex.glsl b/src/shaders/raster_particle_update.vertex.glsl new file mode 100644 index 00000000000..f4b1e6a2cb6 --- /dev/null +++ b/src/shaders/raster_particle_update.vertex.glsl @@ -0,0 +1,8 @@ +in vec2 a_pos; + +out vec2 v_tex_coord; + +void main() { + v_tex_coord = 0.5 * (a_pos + vec2(1.0)); + gl_Position = vec4(a_pos, 0.0, 1.0); +} diff --git a/src/shaders/shaders.js b/src/shaders/shaders.js index 76b1b247e25..2e16560bb13 100644 --- a/src/shaders/shaders.js +++ b/src/shaders/shaders.js @@ -1,6 +1,5 @@ - +// @noflow // Disable Flow annotations here because Flow doesn't support importing GLSL files -/* eslint-disable flowtype/require-valid-file-annotation */ import preludeCommon from './_prelude.glsl'; import preludeFrag from './_prelude.fragment.glsl'; @@ -47,6 +46,14 @@ import linePatternFrag from './line_pattern.fragment.glsl'; import linePatternVert from './line_pattern.vertex.glsl'; import rasterFrag from './raster.fragment.glsl'; import rasterVert from './raster.vertex.glsl'; +import rasterParticleFrag from './raster_particle.fragment.glsl'; +import rasterParticleVert from './raster_particle.vertex.glsl'; +import rasterParticleDrawFrag from './raster_particle_draw.fragment.glsl'; +import rasterParticleDrawVert from './raster_particle_draw.vertex.glsl'; +import rasterParticleTextureFrag from './raster_particle_texture.fragment.glsl'; +import rasterParticleTextureVert from './raster_particle_texture.vertex.glsl'; +import rasterParticleUpdateFrag from './raster_particle_update.fragment.glsl'; +import rasterParticleUpdateVert from './raster_particle_update.vertex.glsl'; import symbolIconFrag from './symbol_icon.fragment.glsl'; import symbolIconVert from './symbol_icon.vertex.glsl'; import symbolSDFFrag from './symbol_sdf.fragment.glsl'; @@ -64,6 +71,8 @@ import preludeTerrainVert from './_prelude_terrain.vertex.glsl'; import preludeFogVert from './_prelude_fog.vertex.glsl'; import preludeFogFrag from './_prelude_fog.fragment.glsl'; import preludeLighting from './_prelude_lighting.glsl'; +import preludeRasterArrayFrag from './_prelude_raster_array.glsl'; +import preludeRasterParticleFrag from './_prelude_raster_particle.glsl'; import skyboxCaptureFrag from './skybox_capture.fragment.glsl'; import skyboxCaptureVert from './skybox_capture.vertex.glsl'; import globeFrag from './globe_raster.fragment.glsl'; @@ -88,16 +97,22 @@ import preludeShadowFrag from '../../3d-style/shaders/_prelude_shadow.fragment.g export let preludeTerrain = {}; export let preludeFog = {}; export let preludeShadow = {}; +export let preludeRasterArray = {}; +export let preludeRasterParticle = {}; const commonDefines = []; parseUsedPreprocessorDefines(preludeCommon, commonDefines); +parseUsedPreprocessorDefines(preludeVert, commonDefines); +parseUsedPreprocessorDefines(preludeFrag, commonDefines); export const includeMap = { "_prelude_fog.vertex.glsl": preludeFogVert, "_prelude_terrain.vertex.glsl": preludeTerrainVert, "_prelude_shadow.vertex.glsl": preludeShadowVert, "_prelude_fog.fragment.glsl": preludeFogFrag, "_prelude_shadow.fragment.glsl": preludeShadowFrag, - "_prelude_lighting.glsl": preludeLighting + "_prelude_lighting.glsl": preludeLighting, + "_prelude_raster_array.glsl": preludeRasterArrayFrag, + "_prelude_raster_particle.glsl": preludeRasterParticleFrag }; // Populated during precompilation const defineMap = {}; @@ -105,47 +120,15 @@ const defineMap = {}; preludeTerrain = compile('', preludeTerrainVert); preludeFog = compile(preludeFogFrag, preludeFogVert); preludeShadow = compile(preludeShadowFrag, preludeShadowVert); +preludeRasterArray = compile(preludeRasterArrayFrag, ''); +preludeRasterParticle = compile(preludeRasterParticleFrag, ''); export const prelude = compile(preludeFrag, preludeVert); export const preludeCommonSource = preludeCommon; export const preludeLightingSource = preludeLighting; -export const preludeVertPrecisionQualifiers = ` -#ifdef GL_ES -precision highp float; -#else - -#if !defined(lowp) -#define lowp -#endif - -#if !defined(mediump) -#define mediump -#endif - -#if !defined(highp) -#define highp -#endif - -#endif`; -export const preludeFragPrecisionQualifiers = ` -#ifdef GL_ES -precision mediump float; -#else - -#if !defined(lowp) -#define lowp -#endif - -#if !defined(mediump) -#define mediump -#endif - -#if !defined(highp) -#define highp -#endif - -#endif`; +export const preludeVertPrecisionQualifiers = `precision highp float;`; +export const preludeFragPrecisionQualifiers = `precision mediump float;`; export default { background: compile(backgroundFrag, backgroundVert), @@ -171,6 +154,10 @@ export default { line: compile(lineFrag, lineVert), linePattern: compile(linePatternFrag, linePatternVert), raster: compile(rasterFrag, rasterVert), + rasterParticle: compile(rasterParticleFrag, rasterParticleVert), + rasterParticleDraw: compile(rasterParticleDrawFrag, rasterParticleDrawVert), + rasterParticleTexture: compile(rasterParticleTextureFrag, rasterParticleTextureVert), + rasterParticleUpdate: compile(rasterParticleUpdateFrag, rasterParticleUpdateVert), symbolIcon: compile(symbolIconFrag, symbolIconVert), symbolSDF: compile(symbolSDFFrag, symbolSDFVert), symbolTextAndIcon: compile(symbolTextAndIconFrag, symbolTextAndIconVert), @@ -212,15 +199,18 @@ export function parseUsedPreprocessorDefines(source, defines) { export function compile(fragmentSource, vertexSource) { const includeRegex = /#include\s+"([^"]+)"/g; const pragmaRegex = /#pragma mapbox: ([\w\-]+) ([\w]+) ([\w]+) ([\w]+)/g; - const attributeRegex = /attribute(\S*) (highp |mediump |lowp )?([\w]+) ([\w]+)/g; + const attributeRegex = /(attribute(\S*)|(^\s*|;)in) (highp |mediump |lowp )?([\w]+) ([\w]+)/gm; let staticAttributes = vertexSource.match(attributeRegex); - // remove duplicates as Safari does not support lookbehind in regex - // so we need to get rid of initialize-* expressions + if (staticAttributes) { - staticAttributes = staticAttributes.filter((element, index) => { - return staticAttributes.indexOf(element) === index; + staticAttributes = staticAttributes.map((str) => { + const tokens = str.split(' '); + return tokens[tokens.length - 1]; }); + // remove duplicates as Safari does not support lookbehind in regex + // so we need to get rid of initialize-* expressions + staticAttributes = [...new Set(staticAttributes)]; } const fragmentPragmas = {}; @@ -235,6 +225,11 @@ export function compile(fragmentSource, vertexSource) { return ''; }); + if (vertexSource.includes("flat out")) { + console.error(`The usage of "flat" qualifier is disallowed, see: https://bugs.webkit.org/show_bug.cgi?id=268071`); + return; + } + let usedDefines = [...commonDefines]; parseUsedPreprocessorDefines(fragmentSource, usedDefines); parseUsedPreprocessorDefines(vertexSource, usedDefines); @@ -254,7 +249,7 @@ export function compile(fragmentSource, vertexSource) { if (operation === 'define') { return ` #ifndef HAS_UNIFORM_u_${name} -varying ${precision} ${type} ${name}; +in ${precision} ${type} ${name}; #else uniform ${precision} ${type} u_${name}; #endif @@ -268,7 +263,7 @@ uniform ${precision} ${type} u_${name}; } else if (operation === 'define-attribute') { return ` #ifdef HAS_ATTRIBUTE_a_${name} - varying ${precision} ${type} ${name}; + in ${precision} ${type} ${name}; #endif `; } else if (operation === 'initialize-attribute') { @@ -284,7 +279,7 @@ uniform ${precision} ${type} u_${name}; if (operation === 'define-attribute-vertex-shader-only') { return ` #ifdef HAS_ATTRIBUTE_a_${name} -attribute ${precision} ${type} a_${name}; +in ${precision} ${type} a_${name}; #endif `; } else if (fragmentPragmas[name]) { @@ -292,8 +287,8 @@ attribute ${precision} ${type} a_${name}; return ` #ifndef HAS_UNIFORM_u_${name} uniform lowp float u_${name}_t; -attribute ${precision} ${attrType} a_${name}; -varying ${precision} ${type} ${name}; +in ${precision} ${attrType} a_${name}; +out ${precision} ${type} ${name}; #else uniform ${precision} ${type} u_${name}; #endif @@ -320,8 +315,8 @@ uniform ${precision} ${type} u_${name}; } else if (operation === 'define-attribute') { return ` #ifdef HAS_ATTRIBUTE_a_${name} - attribute ${precision} ${type} a_${name}; - varying ${precision} ${type} ${name}; + in ${precision} ${type} a_${name}; + out ${precision} ${type} ${name}; #endif `; } else if (operation === 'initialize-attribute') { @@ -336,7 +331,7 @@ uniform ${precision} ${type} u_${name}; return ` #ifndef HAS_UNIFORM_u_${name} uniform lowp float u_${name}_t; -attribute ${precision} ${attrType} a_${name}; +in ${precision} ${attrType} a_${name}; #else uniform ${precision} ${type} u_${name}; #endif @@ -345,10 +340,10 @@ uniform ${precision} ${type} u_${name}; if (unpackType === 'mat4') { return ` #ifdef INSTANCED_ARRAYS -attribute vec4 a_${name}0; -attribute vec4 a_${name}1; -attribute vec4 a_${name}2; -attribute vec4 a_${name}3; +in vec4 a_${name}0; +in vec4 a_${name}1; +in vec4 a_${name}2; +in vec4 a_${name}3; #else uniform ${precision} ${type} u_${name}; #endif @@ -356,7 +351,7 @@ uniform ${precision} ${type} u_${name}; } else { return ` #ifdef INSTANCED_ARRAYS -attribute ${precision} ${attrType} a_${name}; +in ${precision} ${attrType} a_${name}; #else uniform ${precision} ${type} u_${name}; #endif diff --git a/src/shaders/skybox.fragment.glsl b/src/shaders/skybox.fragment.glsl index 9e603832143..d3bbf803d10 100644 --- a/src/shaders/skybox.fragment.glsl +++ b/src/shaders/skybox.fragment.glsl @@ -2,7 +2,7 @@ // [1] Banding in games http://loopit.dk/banding_in_games.pdf -varying lowp vec3 v_uv; +in lowp vec3 v_uv; uniform lowp samplerCube u_cubemap; uniform lowp float u_opacity; @@ -42,7 +42,7 @@ void main() { // (0.0,1.0) to (-1.0,1.0) on y. The inverse operation is applied when generating. uv.y = map(uv.y, 0.0, 1.0, -1.0, 1.0); - vec3 sky_color = textureCube(u_cubemap, uv).rgb; + vec3 sky_color = texture(u_cubemap, uv).rgb; #ifdef FOG // Apply fog contribution if enabled @@ -55,9 +55,9 @@ void main() { // Add sun disk sky_color += 0.1 * sun_disk(v_uv, u_sun_direction); - gl_FragColor = vec4(sky_color * u_opacity, u_opacity); + glFragColor = vec4(sky_color * u_opacity, u_opacity); #ifdef OVERDRAW_INSPECTOR - gl_FragColor = vec4(1.0); + glFragColor = vec4(1.0); #endif } diff --git a/src/shaders/skybox.vertex.glsl b/src/shaders/skybox.vertex.glsl index b4d4cfb8932..e2e3afc274c 100644 --- a/src/shaders/skybox.vertex.glsl +++ b/src/shaders/skybox.vertex.glsl @@ -1,8 +1,8 @@ -attribute highp vec3 a_pos_3f; +in highp vec3 a_pos_3f; uniform lowp mat4 u_matrix; -varying highp vec3 v_uv; +out highp vec3 v_uv; void main() { const mat3 half_neg_pi_around_x = mat3(1.0, 0.0, 0.0, diff --git a/src/shaders/skybox_capture.fragment.glsl b/src/shaders/skybox_capture.fragment.glsl index da483aff7b5..92ca1d11fd5 100644 --- a/src/shaders/skybox_capture.fragment.glsl +++ b/src/shaders/skybox_capture.fragment.glsl @@ -2,7 +2,7 @@ // [2] Earth Fact Sheet https://nssdc.gsfc.nasa.gov/planetary/factsheet/earthfact.html // [3] Tonemapping Operators http://filmicworlds.com/blog/filmic-tonemapping-operators -varying highp vec3 v_position; +in highp vec3 v_position; uniform highp float u_sun_intensity; uniform highp float u_luminance; @@ -10,9 +10,7 @@ uniform lowp vec3 u_sun_direction; uniform highp vec4 u_color_tint_r; uniform highp vec4 u_color_tint_m; -#ifdef GL_ES precision highp float; -#endif // [1] equation (1) section 2.1. for λ = (680, 550, 440) nm, // which corresponds to scattering coefficients at sea level @@ -137,5 +135,5 @@ void main() { float white_scale = 1.0748724675633854; // 1.0 / uncharted2_tonemap(1000.0) color = uncharted2_tonemap((log2(2.0 / pow(u_luminance, 4.0))) * color) * white_scale; - gl_FragColor = vec4(color, 1.0); + glFragColor = vec4(color, 1.0); } diff --git a/src/shaders/skybox_capture.vertex.glsl b/src/shaders/skybox_capture.vertex.glsl index 6427e8c1951..202f0a42bb6 100644 --- a/src/shaders/skybox_capture.vertex.glsl +++ b/src/shaders/skybox_capture.vertex.glsl @@ -1,8 +1,8 @@ -attribute highp vec3 a_pos_3f; +in highp vec3 a_pos_3f; uniform mat3 u_matrix_3f; -varying highp vec3 v_position; +out highp vec3 v_position; float map(float value, float start, float end, float new_start, float new_end) { return ((value - start) * (new_end - new_start)) / (end - start) + new_start; diff --git a/src/shaders/skybox_gradient.fragment.glsl b/src/shaders/skybox_gradient.fragment.glsl index 63105808542..422cefefe8e 100644 --- a/src/shaders/skybox_gradient.fragment.glsl +++ b/src/shaders/skybox_gradient.fragment.glsl @@ -1,6 +1,6 @@ #include "_prelude_fog.fragment.glsl" -varying highp vec3 v_uv; +in highp vec3 v_uv; uniform lowp sampler2D u_color_ramp; uniform highp vec3 u_center_direction; @@ -10,7 +10,7 @@ uniform highp float u_temporal_offset; void main() { float progress = acos(dot(normalize(v_uv), u_center_direction)) / u_radius; - vec4 color = texture2D(u_color_ramp, vec2(progress, 0.5)); + vec4 color = texture(u_color_ramp, vec2(progress, 0.5)); #ifdef FOG // Apply fog contribution if enabled, make sure to un/post multiply alpha before/after @@ -24,9 +24,9 @@ void main() { // Dither color.rgb = dither(color.rgb, gl_FragCoord.xy + u_temporal_offset); - gl_FragColor = color; + glFragColor = color; #ifdef OVERDRAW_INSPECTOR - gl_FragColor = vec4(1.0); + glFragColor = vec4(1.0); #endif } diff --git a/src/shaders/stars.fragment.glsl b/src/shaders/stars.fragment.glsl index 19c36c57add..54519b95edf 100644 --- a/src/shaders/stars.fragment.glsl +++ b/src/shaders/stars.fragment.glsl @@ -1,5 +1,5 @@ -varying highp vec2 v_uv; -varying mediump float v_intensity; +in highp vec2 v_uv; +in mediump float v_intensity; // TODO: // - check other shapes compared to circle, e.g. astroid @@ -30,7 +30,7 @@ void main() { float alpha = shapeCircle(v_uv); vec3 color = vec3(1.0, 1.0, 1.0); alpha *= v_intensity; - gl_FragColor = vec4(color * alpha, alpha); + glFragColor = vec4(color * alpha, alpha); HANDLE_WIREFRAME_DEBUG; } diff --git a/src/shaders/stars.vertex.glsl b/src/shaders/stars.vertex.glsl index e72dddbbc92..0266e1e6252 100644 --- a/src/shaders/stars.vertex.glsl +++ b/src/shaders/stars.vertex.glsl @@ -1,11 +1,11 @@ // Position -attribute vec3 a_pos_3f; +in vec3 a_pos_3f; // Offset from center ([-1, -1], ...) -attribute vec2 a_uv; +in vec2 a_uv; // Per-star size multiplier -attribute float a_size_scale; +in float a_size_scale; // Per-star transparency multiplier -attribute float a_fade_opacity; +in float a_fade_opacity; // mvp uniform mat4 u_matrix; @@ -17,8 +17,8 @@ uniform vec3 u_right; // Global stars transparency multiplier uniform float u_intensity_multiplier; -varying highp vec2 v_uv; -varying mediump float v_intensity; +out highp vec2 v_uv; +out mediump float v_intensity; void main() { v_uv = a_uv; diff --git a/src/shaders/symbol_icon.fragment.glsl b/src/shaders/symbol_icon.fragment.glsl index f7189427ce3..da589d0d36a 100644 --- a/src/shaders/symbol_icon.fragment.glsl +++ b/src/shaders/symbol_icon.fragment.glsl @@ -5,10 +5,14 @@ uniform sampler2D u_texture; uniform float u_icon_transition; #endif -varying float v_fade_opacity; -varying vec2 v_tex_a; +in float v_fade_opacity; +in vec2 v_tex_a; #ifdef ICON_TRANSITION -varying vec2 v_tex_b; +in vec2 v_tex_b; +#endif + +#ifdef COLOR_ADJUSTMENT +uniform mat4 u_color_adj_mat; #endif #pragma mapbox: define lowp float opacity @@ -22,21 +26,27 @@ void main() { vec4 out_color; #ifdef ICON_TRANSITION - vec4 a = texture2D(u_texture, v_tex_a) * (1.0 - u_icon_transition); - vec4 b = texture2D(u_texture, v_tex_b) * u_icon_transition; - out_color = (a + b) * alpha; + vec4 a = texture(u_texture, v_tex_a) * (1.0 - u_icon_transition); + vec4 b = texture(u_texture, v_tex_b) * u_icon_transition; + out_color = (a + b); #else - out_color = texture2D(u_texture, v_tex_a) * alpha; + out_color = texture(u_texture, v_tex_a); #endif +#ifdef COLOR_ADJUSTMENT + out_color = u_color_adj_mat * out_color; +#endif + + out_color *= alpha; + #ifdef LIGHTING_3D_MODE out_color = apply_lighting_with_emission_ground(out_color, emissive_strength); #endif - gl_FragColor = out_color; + glFragColor = out_color; #ifdef OVERDRAW_INSPECTOR - gl_FragColor = vec4(1.0); + glFragColor = vec4(1.0); #endif HANDLE_WIREFRAME_DEBUG; diff --git a/src/shaders/symbol_icon.vertex.glsl b/src/shaders/symbol_icon.vertex.glsl index 715415935aa..7e734f1df8f 100644 --- a/src/shaders/symbol_icon.vertex.glsl +++ b/src/shaders/symbol_icon.vertex.glsl @@ -1,19 +1,19 @@ #include "_prelude_terrain.vertex.glsl" -attribute vec4 a_pos_offset; -attribute vec4 a_tex_size; -attribute vec4 a_pixeloffset; -attribute vec4 a_projected_pos; -attribute float a_fade_opacity; +in vec4 a_pos_offset; +in vec4 a_tex_size; +in vec4 a_pixeloffset; +in vec4 a_projected_pos; +in float a_fade_opacity; #ifdef Z_OFFSET -attribute float a_z_offset; +in float a_z_offset; #endif #ifdef PROJECTION_GLOBE_VIEW -attribute vec3 a_globe_anchor; -attribute vec3 a_globe_normal; +in vec3 a_globe_anchor; +in vec3 a_globe_normal; #endif #ifdef ICON_TRANSITION -attribute vec2 a_texb; +in vec2 a_texb; #endif uniform bool u_is_size_zoom_constant; @@ -45,11 +45,11 @@ uniform vec3 u_ecef_origin; uniform mat4 u_tile_matrix; #endif -varying vec2 v_tex_a; +out vec2 v_tex_a; #ifdef ICON_TRANSITION -varying vec2 v_tex_b; +out vec2 v_tex_b; #endif -varying float v_fade_opacity; +out float v_fade_opacity; #pragma mapbox: define lowp float opacity #pragma mapbox: define lowp float emissive_strength diff --git a/src/shaders/symbol_sdf.fragment.glsl b/src/shaders/symbol_sdf.fragment.glsl index d6165acec27..ff29a84fab9 100644 --- a/src/shaders/symbol_sdf.fragment.glsl +++ b/src/shaders/symbol_sdf.fragment.glsl @@ -8,11 +8,9 @@ uniform lowp float u_device_pixel_ratio; uniform bool u_is_text; uniform bool u_is_halo; -#if __VERSION__ >= 300 -flat varying float v_draw_halo; -#endif -varying vec2 v_data0; -varying vec3 v_data1; +in float v_draw_halo; +in vec2 v_data0; +in vec3 v_data1; #pragma mapbox: define highp vec4 fill_color #pragma mapbox: define highp vec4 halo_color @@ -42,19 +40,14 @@ void main() { highp float gamma = EDGE_GAMMA / (fontScale * u_gamma_scale); lowp float buff = (256.0 - 64.0) / 256.0; - bool draw_halo; -#if __VERSION__ >= 300 - draw_halo = v_draw_halo > 0.0; -#else - draw_halo = u_is_halo; -#endif + bool draw_halo = v_draw_halo > 0.0; if (draw_halo) { color = halo_color; gamma = (halo_blur * 1.19 / SDF_PX + EDGE_GAMMA) / (fontScale * u_gamma_scale); buff = (6.0 - halo_width / fontScale) / SDF_PX; } - lowp float dist = texture2D(u_texture, tex).a; + lowp float dist = texture(u_texture, tex).r; highp float gamma_scaled = gamma * gamma_scale; highp float alpha = smoothstep(buff - gamma_scaled, buff + gamma_scaled, dist); @@ -64,10 +57,10 @@ void main() { out_color = apply_lighting_with_emission_ground(out_color, emissive_strength); #endif - gl_FragColor = out_color; + glFragColor = out_color; #ifdef OVERDRAW_INSPECTOR - gl_FragColor = vec4(1.0); + glFragColor = vec4(1.0); #endif HANDLE_WIREFRAME_DEBUG; diff --git a/src/shaders/symbol_sdf.vertex.glsl b/src/shaders/symbol_sdf.vertex.glsl index 5629aa16a1a..d1ed13d517d 100644 --- a/src/shaders/symbol_sdf.vertex.glsl +++ b/src/shaders/symbol_sdf.vertex.glsl @@ -1,16 +1,16 @@ #include "_prelude_terrain.vertex.glsl" -attribute vec4 a_pos_offset; -attribute vec4 a_tex_size; -attribute vec4 a_pixeloffset; -attribute vec4 a_projected_pos; -attribute float a_fade_opacity; +in vec4 a_pos_offset; +in vec4 a_tex_size; +in vec4 a_pixeloffset; +in vec4 a_projected_pos; +in float a_fade_opacity; #ifdef Z_OFFSET -attribute float a_z_offset; +in float a_z_offset; #endif #ifdef PROJECTION_GLOBE_VIEW -attribute vec3 a_globe_anchor; -attribute vec3 a_globe_normal; +in vec3 a_globe_anchor; +in vec3 a_globe_normal; #endif // contents of a_size vary based on the type of property value @@ -47,11 +47,9 @@ uniform vec3 u_ecef_origin; uniform mat4 u_tile_matrix; #endif -#if __VERSION__ >= 300 -flat varying float v_draw_halo; -#endif -varying vec2 v_data0; -varying vec3 v_data1; +out float v_draw_halo; +out vec2 v_data0; +out vec3 v_data1; #pragma mapbox: define highp vec4 fill_color #pragma mapbox: define highp vec4 halo_color @@ -196,10 +194,8 @@ void main() { #endif float gamma_scale = gl_Position.w; -#if __VERSION__ >= 300 // Cast to float is required to fix a rendering error in Swiftshader v_draw_halo = (u_is_halo && float(gl_InstanceID) == 0.0) ? 1.0 : 0.0; -#endif v_data0 = a_tex / u_texsize; v_data1 = vec3(gamma_scale, size, out_fade_opacity); diff --git a/src/shaders/symbol_text_and_icon.fragment.glsl b/src/shaders/symbol_text_and_icon.fragment.glsl index 266ceb35177..8aa8cafd3e7 100644 --- a/src/shaders/symbol_text_and_icon.fragment.glsl +++ b/src/shaders/symbol_text_and_icon.fragment.glsl @@ -11,11 +11,9 @@ uniform highp float u_gamma_scale; uniform lowp float u_device_pixel_ratio; uniform bool u_is_halo; -#if __VERSION__ >= 300 -flat varying float v_draw_halo; -#endif -varying vec4 v_data0; -varying vec4 v_data1; +in float v_draw_halo; +in vec4 v_data0; +in vec4 v_data1; #pragma mapbox: define highp vec4 fill_color #pragma mapbox: define highp vec4 halo_color @@ -37,10 +35,10 @@ void main() { if (v_data1.w == ICON) { vec2 tex_icon = v_data0.zw; lowp float alpha = opacity * fade_opacity; - gl_FragColor = texture2D(u_texture_icon, tex_icon) * alpha; + glFragColor = texture(u_texture_icon, tex_icon) * alpha; #ifdef OVERDRAW_INSPECTOR - gl_FragColor = vec4(1.0); + glFragColor = vec4(1.0); #endif return; } @@ -58,19 +56,14 @@ void main() { highp float gamma = EDGE_GAMMA / (fontScale * u_gamma_scale); lowp float buff = (256.0 - 64.0) / 256.0; - bool draw_halo; -#if __VERSION__ >= 300 - draw_halo = v_draw_halo > 0.0; -#else - draw_halo = u_is_halo; -#endif + bool draw_halo = v_draw_halo > 0.0; if (draw_halo) { color = halo_color; gamma = (halo_blur * 1.19 / SDF_PX + EDGE_GAMMA) / (fontScale * u_gamma_scale); buff = (6.0 - halo_width / fontScale) / SDF_PX; } - lowp float dist = texture2D(u_texture, tex).a; + lowp float dist = texture(u_texture, tex).r; highp float gamma_scaled = gamma * gamma_scale; highp float alpha = smoothstep(buff - gamma_scaled, buff + gamma_scaled, dist); @@ -80,10 +73,10 @@ void main() { out_color = apply_lighting_with_emission_ground(out_color, emissive_strength); #endif - gl_FragColor = out_color; + glFragColor = out_color; #ifdef OVERDRAW_INSPECTOR - gl_FragColor = vec4(1.0); + glFragColor = vec4(1.0); #endif HANDLE_WIREFRAME_DEBUG; diff --git a/src/shaders/symbol_text_and_icon.vertex.glsl b/src/shaders/symbol_text_and_icon.vertex.glsl index 7ef283181a5..2d440f5ace7 100644 --- a/src/shaders/symbol_text_and_icon.vertex.glsl +++ b/src/shaders/symbol_text_and_icon.vertex.glsl @@ -1,15 +1,15 @@ #include "_prelude_terrain.vertex.glsl" -attribute vec4 a_pos_offset; -attribute vec4 a_tex_size; -attribute vec4 a_projected_pos; -attribute float a_fade_opacity; +in vec4 a_pos_offset; +in vec4 a_tex_size; +in vec4 a_projected_pos; +in float a_fade_opacity; #ifdef Z_OFFSET -attribute float a_z_offset; +in float a_z_offset; #endif #ifdef PROJECTION_GLOBE_VIEW -attribute vec3 a_globe_anchor; -attribute vec3 a_globe_normal; +in vec3 a_globe_anchor; +in vec3 a_globe_normal; #endif // contents of a_size vary based on the type of property value @@ -47,11 +47,9 @@ uniform vec3 u_ecef_origin; uniform mat4 u_tile_matrix; #endif -#if __VERSION__ >= 300 -flat varying float v_draw_halo; -#endif -varying vec4 v_data0; -varying vec4 v_data1; +out float v_draw_halo; +out vec4 v_data0; +out vec4 v_data1; #pragma mapbox: define highp vec4 fill_color #pragma mapbox: define highp vec4 halo_color @@ -190,10 +188,8 @@ void main() { #endif float gamma_scale = gl_Position.w; -#if __VERSION__ >= 300 // Cast to float is required to fix a rendering error in Swiftshader v_draw_halo = (u_is_halo && float(gl_InstanceID) == 0.0) ? 1.0 : 0.0; -#endif v_data0.xy = a_tex / u_texsize; v_data0.zw = a_tex / u_texsize_icon; diff --git a/src/shaders/terrain_depth.fragment.glsl b/src/shaders/terrain_depth.fragment.glsl index 064ef75ff7a..004bce42d48 100644 --- a/src/shaders/terrain_depth.fragment.glsl +++ b/src/shaders/terrain_depth.fragment.glsl @@ -1,9 +1,7 @@ -#ifdef GL_ES precision highp float; -#endif -varying float v_depth; +in float v_depth; void main() { - gl_FragColor = pack_depth(v_depth); + glFragColor = pack_depth(v_depth); } diff --git a/src/shaders/terrain_depth.vertex.glsl b/src/shaders/terrain_depth.vertex.glsl index 3c87f9a65fd..f2ebb5cdc73 100644 --- a/src/shaders/terrain_depth.vertex.glsl +++ b/src/shaders/terrain_depth.vertex.glsl @@ -1,12 +1,12 @@ #include "_prelude_terrain.vertex.glsl" uniform mat4 u_matrix; -attribute vec2 a_pos; +in vec2 a_pos; -varying float v_depth; +out float v_depth; void main() { float elevation = elevation(a_pos); gl_Position = u_matrix * vec4(a_pos, elevation, 1.0); v_depth = gl_Position.z / gl_Position.w; -} \ No newline at end of file +} diff --git a/src/shaders/terrain_raster.fragment.glsl b/src/shaders/terrain_raster.fragment.glsl index 86f5e066508..a3a06df83e4 100644 --- a/src/shaders/terrain_raster.fragment.glsl +++ b/src/shaders/terrain_raster.fragment.glsl @@ -3,22 +3,21 @@ #include "_prelude_lighting.glsl" uniform sampler2D u_image0; -varying vec2 v_pos0; +in vec2 v_pos0; #ifdef FOG -varying float v_fog_opacity; +in float v_fog_opacity; #endif #ifdef RENDER_SHADOWS -varying vec4 v_pos_light_view_0; -varying vec4 v_pos_light_view_1; -varying float v_depth; +in vec4 v_pos_light_view_0; +in vec4 v_pos_light_view_1; #endif uniform vec3 u_ground_shadow_factor; void main() { - vec4 image_color = texture2D(u_image0, v_pos0); + vec4 image_color = texture(u_image0, v_pos0); vec4 color; #ifdef LIGHTING_3D_MODE @@ -27,7 +26,7 @@ void main() { #ifdef RENDER_SHADOWS float cutoffOpacity = 1.0; #ifdef RENDER_CUTOFF - cutoffOpacity = cutoff_opacity(u_cutoff_params, v_depth); + cutoffOpacity = cutoff_opacity(u_cutoff_params, 1.0 / gl_FragCoord.w); #endif // RENDER_CUTOFF #ifdef LIGHTING_3D_ALPHA_EMISSIVENESS // Drape texture also contains the flood light color already @@ -37,7 +36,7 @@ void main() { vec3 unlit_base = image_color.rgb * (1.0 - image_color.a); vec3 emissive_base = image_color.rgb * image_color.a; float ndotl = u_shadow_direction.z; - float occlusion = ndotl < 0.0 ? 1.0 : shadow_occlusion(v_pos_light_view_0, v_pos_light_view_1, v_depth, 0.0); + float occlusion = ndotl < 0.0 ? 1.0 : shadow_occlusion(v_pos_light_view_0, v_pos_light_view_1, 1.0 / gl_FragCoord.w, 0.0); ndotl = max(0.0, ndotl); // "lit" uses pretty much "shadowed_light_factor_normal_unbiased" as the directional component. vec3 lit = apply_lighting(unlit_base, normal, mix(1.0, (1.0 - (u_shadow_intensity * occlusion)) * ndotl, cutoffOpacity)); @@ -45,7 +44,7 @@ void main() { color.rgb = lit + emissive; color.a = 1.0; #else // LIGHTING_3D_ALPHA_EMISSIVENESS - float lighting_factor = shadowed_light_factor_normal_unbiased(normal, v_pos_light_view_0, v_pos_light_view_1, v_depth); + float lighting_factor = shadowed_light_factor_normal_unbiased(normal, v_pos_light_view_0, v_pos_light_view_1, 1.0 / gl_FragCoord.w); color = apply_lighting(image_color, normal, mix(1.0, lighting_factor, cutoffOpacity)); #endif // !LIGHTING_3D_ALPHA_EMISSIVENESS #else // RENDER_SHADOWS @@ -68,9 +67,9 @@ void main() { color = fog_dither(fog_apply_from_vert(color, v_fog_opacity)); #endif #endif - gl_FragColor = color; + glFragColor = color; #ifdef OVERDRAW_INSPECTOR - gl_FragColor = vec4(1.0); + glFragColor = vec4(1.0); #endif HANDLE_WIREFRAME_DEBUG; diff --git a/src/shaders/terrain_raster.vertex.glsl b/src/shaders/terrain_raster.vertex.glsl index b92cfb0528e..4d00848432a 100644 --- a/src/shaders/terrain_raster.vertex.glsl +++ b/src/shaders/terrain_raster.vertex.glsl @@ -4,20 +4,20 @@ uniform mat4 u_matrix; uniform float u_skirt_height; -attribute vec2 a_pos; +in vec2 a_pos; -varying vec2 v_pos0; +out vec2 v_pos0; #ifdef FOG -varying float v_fog_opacity; +out float v_fog_opacity; #endif #ifdef RENDER_SHADOWS uniform mat4 u_light_matrix_0; uniform mat4 u_light_matrix_1; -varying vec4 v_pos_light_view_0; -varying vec4 v_pos_light_view_1; -varying float v_depth; +out vec4 v_pos_light_view_0; +out vec4 v_pos_light_view_1; +out float v_depth; #endif void main() { @@ -40,6 +40,5 @@ void main() { vec3 pos = vec3(decodedPos, elevation); v_pos_light_view_0 = u_light_matrix_0 * vec4(pos, 1.); v_pos_light_view_1 = u_light_matrix_1 * vec4(pos, 1.); - v_depth = gl_Position.w; #endif } diff --git a/src/source/building_index.js b/src/source/building_index.js index 7247a70bdff..3ea09db1124 100644 --- a/src/source/building_index.js +++ b/src/source/building_index.js @@ -1,51 +1,71 @@ // @flow -import Tiled3dModelBucket from '../../3d-style/data/bucket/tiled_3d_model_bucket.js'; import FillExtrusionBucket from '../data/bucket/fill_extrusion_bucket.js'; import StyleLayer from '../style/style_layer.js'; import EXTENT from '../style-spec/data/extent.js'; -import type {Bucket} from '../data/bucket.js'; import SymbolBucket from '../data/bucket/symbol_bucket.js'; -import {OverscaledTileID} from './tile_id.js'; import FillExtrusionStyleLayer from '../style/style_layer/fill_extrusion_style_layer.js'; +import {OverscaledTileID} from './tile_id.js'; + import type Style from '../style/style.js'; +import type Tiled3dModelBucket from '../../3d-style/data/bucket/tiled_3d_model_bucket.js'; +import type {Bucket} from '../data/bucket.js'; +import ModelStyleLayer from '../../3d-style/style/style_layer/model_style_layer.js'; class BuildingIndex { style: Style; - layers: Array; + layers: Array<{layer: StyleLayer, visible: boolean}>; currentBuildingBuckets: Array<{bucket: ?Bucket, tileID: OverscaledTileID, verticalScale: number}>; + layersGotHidden: boolean; // when layer're hidden since the last frame, don't keep previous elevation, while loading tiles. constructor(style: Style) { this.style = style; + this.layersGotHidden = false; + this.layers = []; } processLayersChanged() { this.layers = []; + const visible = false, visibilityChanged = false; for (const layerId in this.style._mergedLayers) { const layer = this.style._mergedLayers[layerId]; if (layer.type === 'fill-extrusion') { - this.layers.push(layer); + this.layers.push({layer, visible, visibilityChanged}); } else if (layer.type === 'model') { const source = this.style.getLayerSource(layer); if (source && source.type === 'batched-model') { - this.layers.push(layer); + this.layers.push({layer, visible, visibilityChanged}); } } } } + // Check if some of the building layers are disabled or with opacity evaluated to 0. + onNewFrame(zoom: number) { + this.layersGotHidden = false; + for (const l of this.layers) { + const layer = l.layer; + let visible = false; + if (layer.type === 'fill-extrusion') { + visible = !layer.isHidden(zoom) && ((layer: any): FillExtrusionStyleLayer).paint.get('fill-extrusion-opacity') > 0.0; + } else if (layer.type === 'model') { + visible = !layer.isHidden(zoom) && ((layer: any): ModelStyleLayer).paint.get('model-opacity') > 0.0; + } + this.layersGotHidden = this.layersGotHidden || (!visible && l.visible); + l.visible = visible; + } + } + updateZOffset(symbolBucket: SymbolBucket, tileID: OverscaledTileID) { // prepare lookup from bucket to overlapping buckets of all building layers. this.currentBuildingBuckets = []; - for (let i = 0; i < this.layers.length; ++i) { - const layer = this.layers[i]; + for (const l of this.layers) { + const layer = l.layer; const sourceCache = this.style.getLayerSourceCache(layer); let verticalScale = 1; if (layer.type === 'fill-extrusion') { - // See https://mapbox.atlassian.net/browse/MAPS3D-1159 for more details on why we should take opacity into account. - const opacity = ((layer: any): FillExtrusionStyleLayer).paint.get('fill-extrusion-opacity'); - verticalScale = opacity > 0.0 ? ((layer: any): FillExtrusionStyleLayer).paint.get('fill-extrusion-vertical-scale') : 0; + verticalScale = l.visible ? ((layer: any): FillExtrusionStyleLayer).paint.get('fill-extrusion-vertical-scale') : 0; } let tile = sourceCache ? sourceCache.getTile(tileID) : null; @@ -68,7 +88,8 @@ class BuildingIndex { const currentZOffset = symbolInstance.zOffset; const newZOffset = this._getHeightAtTileOffset(tileID, symbolInstance.tileAnchorX, symbolInstance.tileAnchorY); - symbolInstance.zOffset = newZOffset !== -1 ? newZOffset : currentZOffset; + // When zooming over integer zooms, keep the elevation while loading building buckets. + symbolInstance.zOffset = newZOffset !== Number.NEGATIVE_INFINITY ? newZOffset : currentZOffset; if (!dataChanged && currentZOffset !== symbolInstance.zOffset) { dataChanged = true; @@ -98,10 +119,12 @@ class BuildingIndex { _getHeightAtTileOffset(tid: OverscaledTileID, x: number, y: number): number { let availableHeight; - // use FE data when landmark height is not available. Instead of asuming order, process + let maxFillExtrusionHeight; + // use FE data when landmark height is not available. Instead of assuming order, process // fill extrusions before landmarks for (let i = 0; i < this.layers.length; ++i) { - const layer = this.layers[i]; + const l = this.layers[i]; + const layer = l.layer; if (layer.type !== 'fill-extrusion') continue; const {bucket, tileID, verticalScale} = this.currentBuildingBuckets[i]; if (!bucket) continue; @@ -111,16 +134,20 @@ class BuildingIndex { const b: FillExtrusionBucket = (bucket: any); const heightData = b.getHeightAtTileCoord(tileX, tileY); if (!heightData || heightData.height === undefined) continue; - if (heightData.hidden) { + if (heightData.hidden) { // read height, even if fill extrusion is hidden, until it is used for tiled 3D models. availableHeight = heightData.height; continue; } - return heightData.height * verticalScale; + maxFillExtrusionHeight = Math.max(heightData.height * verticalScale, maxFillExtrusionHeight || 0); + } + if (maxFillExtrusionHeight !== undefined) { + return maxFillExtrusionHeight; } for (let i = 0; i < this.layers.length; ++i) { - const layer = this.layers[i]; - if (layer.type !== 'model') continue; + const l = this.layers[i]; + const layer = l.layer; + if (layer.type !== 'model' || !l.visible) continue; const {bucket, tileID} = this.currentBuildingBuckets[i]; if (!bucket) continue; @@ -132,8 +159,8 @@ class BuildingIndex { if (heightData.height === undefined && availableHeight !== undefined) return Math.min(heightData.maxHeight, availableHeight) * heightData.verticalScale; return (heightData.height || 0) * heightData.verticalScale; } - // We couldn't find a bucket - return -1; + // If we couldn't find a bucket, return Number.NEGATIVE_INFINITY. If a layer got hidden since previous frame, place symbols on ground. + return this.layersGotHidden ? 0 : Number.NEGATIVE_INFINITY; } } diff --git a/src/source/canvas_source.js b/src/source/canvas_source.js index 45d4b5b66e1..6f9e6c32985 100644 --- a/src/source/canvas_source.js +++ b/src/source/canvas_source.js @@ -2,12 +2,11 @@ import ImageSource from './image_source.js'; -import window from '../util/window.js'; import Texture, {UserManagedTexture} from '../render/texture.js'; import {ErrorEvent} from '../util/evented.js'; import ValidationError from '../style-spec/error/validation_error.js'; -import type Map from '../ui/map.js'; +import type {Map} from '../ui/map.js'; import type Dispatcher from '../util/dispatcher.js'; import type {Evented} from '../util/evented.js'; @@ -85,7 +84,7 @@ class CanvasSource extends ImageSource { if (!options.canvas) { this.fire(new ErrorEvent(new ValidationError(`sources.${id}`, null, 'missing required property "canvas"'))); - } else if (typeof options.canvas !== 'string' && !(options.canvas instanceof window.HTMLCanvasElement)) { + } else if (typeof options.canvas !== 'string' && !(options.canvas instanceof HTMLCanvasElement)) { this.fire(new ErrorEvent(new ValidationError(`sources.${id}`, null, '"canvas" must be either a string representing the ID of the canvas element from which to read, or an HTMLCanvasElement instance'))); } @@ -112,9 +111,9 @@ class CanvasSource extends ImageSource { load() { this._loaded = true; if (!this.canvas) { - this.canvas = (this.options.canvas instanceof window.HTMLCanvasElement) ? + this.canvas = (this.options.canvas instanceof HTMLCanvasElement) ? this.options.canvas : - window.document.getElementById(this.options.canvas); + ((document.getElementById(this.options.canvas): any): HTMLCanvasElement); } this.width = this.canvas.width; this.height = this.canvas.height; @@ -185,9 +184,9 @@ class CanvasSource extends ImageSource { * @instance * @memberof CanvasSource * @param {Array>} coordinates Four geographical coordinates, - * represented as arrays of longitude and latitude numbers, which define the corners of the canvas. - * The coordinates start at the top left corner of the canvas and proceed in clockwise order. - * They do not have to represent a rectangle. + * represented as arrays of longitude and latitude numbers, which define the corners of the canvas. + * The coordinates start at the top left corner of the canvas and proceed in clockwise order. + * They do not have to represent a rectangle. * @returns {CanvasSource} Returns itself to allow for method chaining. */ diff --git a/src/source/custom_source.js b/src/source/custom_source.js index 6fbdbfd7a87..d0ca9bf5ec5 100644 --- a/src/source/custom_source.js +++ b/src/source/custom_source.js @@ -1,14 +1,13 @@ // @flow import Tile from './tile.js'; -import window from '../util/window.js'; import Texture from '../render/texture.js'; import TileBounds from './tile_bounds.js'; import {extend, pick} from '../util/util.js'; import {Event, ErrorEvent, Evented} from '../util/evented.js'; import {makeFQID} from '../util/fqid.js'; -import type Map from '../ui/map.js'; +import type {Map} from '../ui/map.js'; import type Dispatcher from '../util/dispatcher.js'; import type {Source} from './source.js'; import type {Callback} from '../types/callback.js'; @@ -17,10 +16,10 @@ import type {OverscaledTileID} from './tile_id.js'; type DataType = 'raster'; function isRaster(data: any): boolean { - return data instanceof window.ImageData || - data instanceof window.HTMLCanvasElement || - data instanceof window.ImageBitmap || - data instanceof window.HTMLImageElement; + return data instanceof ImageData || + data instanceof HTMLCanvasElement || + data instanceof ImageBitmap || + data instanceof HTMLImageElement; } /* eslint-disable jsdoc/check-examples */ @@ -131,7 +130,7 @@ function isRaster(data: any): boolean { * @param {Object} options Options. * @param {AbortSignal} options.signal A signal object that communicates when the map cancels the tile loading request. * @returns {Promise} The promise that resolves to the tile image data as an `HTMLCanvasElement`, `HTMLImageElement`, `ImageData`, `ImageBitmap` or object with `width`, `height`, and `data`. - * If `loadTile` resolves to `undefined`, a map will render an overscaled parent tile in the tile’s space. If `loadTile` resolves to `null`, a map will render nothing in the tile’s space. + * If `loadTile` resolves to `undefined`, a map will render an overscaled parent tile in the tile’s space. If `loadTile` resolves to `null`, a map will render nothing in the tile’s space. */ export type CustomSourceInterface = { id: string; @@ -257,7 +256,7 @@ class CustomSource extends Evented implements Source { loadTile(tile: Tile, callback: Callback): void { const {x, y, z} = tile.tileID.canonical; - const controller = new window.AbortController(); + const controller = new AbortController(); const signal = controller.signal; // $FlowFixMe[prop-missing] diff --git a/src/source/geojson_source.js b/src/source/geojson_source.js index cdd077ba2ca..fc9584fc52c 100644 --- a/src/source/geojson_source.js +++ b/src/source/geojson_source.js @@ -8,7 +8,7 @@ import {ResourceType} from '../util/ajax.js'; import browser from '../util/browser.js'; import type {Source} from './source.js'; -import type Map from '../ui/map.js'; +import type {Map} from '../ui/map.js'; import type Dispatcher from '../util/dispatcher.js'; import type Tile from './tile.js'; import type Actor from '../util/actor.js'; @@ -115,6 +115,7 @@ class GeoJSONSource extends Evented implements Source { this._collectResourceTiming = options.collectResourceTiming; if (options.maxzoom !== undefined) this.maxzoom = options.maxzoom; + if (options.minzoom !== undefined) this.minzoom = options.minzoom; if (options.type) this.type = options.type; if (options.attribution) this.attribution = options.attribution; this.promoteId = options.promoteId; diff --git a/src/source/geojson_worker_source.js b/src/source/geojson_worker_source.js index e77f65bc901..8f43aa5cd16 100644 --- a/src/source/geojson_worker_source.js +++ b/src/source/geojson_worker_source.js @@ -3,7 +3,6 @@ import {getJSON} from '../util/ajax.js'; import {getPerformanceMeasurement} from '../util/performance.js'; -import rewind from '@mapbox/geojson-rewind'; import GeoJSONWrapper from './geojson_wrapper.js'; import vtpbf from 'vt-pbf'; import Supercluster from 'supercluster'; @@ -21,7 +20,7 @@ import type { import type Actor from '../util/actor.js'; import type StyleLayerIndex from '../style/style_layer_index.js'; -import type {LoadVectorDataCallback} from './vector_tile_worker_source.js'; +import type {LoadVectorDataCallback} from './load_vector_tile.js'; import type {RequestParameters, ResponseCallback} from '../util/ajax.js'; import type {Callback} from '../types/callback.js'; import type {GeoJSONFeature} from '@mapbox/geojson-types'; @@ -135,8 +134,6 @@ class GeoJSONWorkerSource extends VectorTileWorkerSource { } else if (typeof data !== 'object') { return callback(new Error(`Input data given to '${params.source}' is not a valid GeoJSON object.`)); } else { - rewind(data, true); - try { if (params.filter) { const compiled = createExpression(params.filter, {type: 'boolean', 'property-type': 'data-driven', overridable: false, transition: false}); diff --git a/src/source/image_source.js b/src/source/image_source.js index 03572710118..53c2038ee80 100644 --- a/src/source/image_source.js +++ b/src/source/image_source.js @@ -2,26 +2,30 @@ import {CanonicalTileID} from './tile_id.js'; import {Event, ErrorEvent, Evented} from '../util/evented.js'; +import {lowerBound, upperBound} from '../util/util.js'; import {getImage, ResourceType} from '../util/ajax.js'; import EXTENT from '../style-spec/data/extent.js'; -import {RasterBoundsArray} from '../data/array_types.js'; +import {RasterBoundsArray, TriangleIndexArray} from '../data/array_types.js'; import boundsAttributes from '../data/bounds_attributes.js'; import SegmentVector from '../data/segment.js'; import Texture, {UserManagedTexture} from '../render/texture.js'; import MercatorCoordinate, {MAX_MERCATOR_LATITUDE} from '../geo/mercator_coordinate.js'; import browser from '../util/browser.js'; import tileTransform, {getTilePoint} from '../geo/projection/tile_transform.js'; +import {GLOBE_VERTEX_GRID_SIZE} from '../geo/projection/globe_constants.js'; import {mat3, vec3} from 'gl-matrix'; -import window from '../util/window.js'; +import LngLat from '../geo/lng_lat.js'; import type {Source} from './source.js'; import type {CanvasSourceSpecification} from './canvas_source.js'; -import type Map from '../ui/map.js'; +import type {Map} from '../ui/map.js'; import type Dispatcher from '../util/dispatcher.js'; import type Tile from './tile.js'; import type {Callback} from '../types/callback.js'; import type {Cancelable} from '../types/cancelable.js'; import type VertexBuffer from '../gl/vertex_buffer.js'; +import type IndexBuffer from '../gl/index_buffer.js'; +import type {ProjectedPoint} from '../geo/projection/projection.js'; import type { ImageSourceSpecification, VideoSourceSpecification @@ -38,24 +42,140 @@ type ImageSourceTexture = {| // perspective correction for texture mapping, see https://github.com/mapbox/mapbox-gl-js/issues/9158 // adapted from https://math.stackexchange.com/a/339033/48653 +// Creates a matrix that maps +// (1, 0, 0) -> (a * x1, a * y1, a) +// (0, 1, 0) -> (b * x2, b * y2, b) +// (0, 0, 1) -> (c * x3, c * y3, c) +// (1, 1, 1) -> (x4, y4, 1) function basisToPoints(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, x4: number, y4: number) { - const m = [x1, x2, x3, y1, y2, y3, 1, 1, 1]; + const m = [x1, y1, 1, x2, y2, 1, x3, y3, 1]; const s = [x4, y4, 1]; const ma = mat3.adjoint([], m); - const [sx, sy, sz] = vec3.transformMat3(s, s, mat3.transpose(ma, ma)); - return mat3.multiply(m, [sx, 0, 0, 0, sy, 0, 0, 0, sz], m); + const [sx, sy, sz] = vec3.transformMat3(s, s, ma); + return mat3.multiply(m, m, [sx, 0, 0, 0, sy, 0, 0, 0, sz]); } -function getPerspectiveTransform(w: number, h: number, x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, x4: number, y4: number) { - const s = basisToPoints(0, 0, w, 0, 0, h, w, h); - const m = basisToPoints(x1, y1, x2, y2, x3, y3, x4, y4); - mat3.multiply(m, mat3.adjoint(s, s), m); +function getTileToTextureTransformMatrix(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, x4: number, y4: number) { + const a = basisToPoints(0, 0, 1, 0, 1, 1, 0, 1); + const b = basisToPoints(x1, y1, x2, y2, x3, y3, x4, y4); + const adjB = mat3.adjoint([], b); + return mat3.multiply(a, a, adjB); +} + +function getTextureToTileTransformMatrix(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, x4: number, y4: number) { + const a = basisToPoints(0, 0, 1, 0, 1, 1, 0, 1); + const b = basisToPoints(x1, y1, x2, y2, x3, y3, x4, y4); + const adjA = mat3.adjoint([], a); + return mat3.multiply(b, b, adjA); +} + +function getPerspectiveTransform(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, x4: number, y4: number) { + const m = getTextureToTileTransformMatrix(x1, y1, x2, y2, x3, y3, x4, y4); return [ - m[6] / m[8] * w / EXTENT, - m[7] / m[8] * h / EXTENT + m[2] / m[8] / EXTENT, + m[5] / m[8] / EXTENT ]; } +function isConvex(coords: [ProjectedPoint, ProjectedPoint, ProjectedPoint, ProjectedPoint]) { + const dx1 = coords[1].x - coords[0].x; + const dy1 = coords[1].y - coords[0].y; + const dx2 = coords[2].x - coords[1].x; + const dy2 = coords[2].y - coords[1].y; + const dx3 = coords[3].x - coords[2].x; + const dy3 = coords[3].y - coords[2].y; + const dx4 = coords[0].x - coords[3].x; + const dy4 = coords[0].y - coords[3].y; + + const crossProduct1 = dx1 * dy2 - dx2 * dy1; + const crossProduct2 = dx2 * dy3 - dx3 * dy2; + const crossProduct3 = dx3 * dy4 - dx4 * dy3; + const crossProduct4 = dx4 * dy1 - dx1 * dy4; + + return (crossProduct1 > 0 && crossProduct2 > 0 && crossProduct3 > 0 && crossProduct4 > 0) || + (crossProduct1 < 0 && crossProduct2 < 0 && crossProduct3 < 0 && crossProduct4 < 0); +} + +function constrainCoordinates(coords: [number, number]) { + return [coords[0], Math.min(Math.max(coords[1], -MAX_MERCATOR_LATITUDE), MAX_MERCATOR_LATITUDE)]; +} + +function constrain(coords: Coordinates) { + return [ + constrainCoordinates(coords[0]), + constrainCoordinates(coords[1]), + constrainCoordinates(coords[2]), + constrainCoordinates(coords[3])]; +} + +function calculateMinAndSize(coords: Coordinates) { + let minX = coords[0][0]; + let maxX = minX; + let minY = coords[0][1]; + let maxY = minY; + + for (let i = 1; i < coords.length; i++) { + if (coords[i][0] < minX) { + minX = coords[i][0]; + } else if (coords[i][0] > maxX) { + maxX = coords[i][0]; + } + if (coords[i][1] < minY) { + minY = coords[i][1]; + } else if (coords[i][1] > maxY) { + maxY = coords[i][1]; + } + } + return [minX, minY, maxX - minX, maxY - minY]; +} + +function calculateMinAndSizeForPoints(coords: ProjectedPoint[]) { + let minX = coords[0].x; + let maxX = minX; + let minY = coords[0].y; + let maxY = minY; + + for (let i = 1; i < coords.length; i++) { + if (coords[i].x < minX) { + minX = coords[i].x; + } else if (coords[i].x > maxX) { + maxX = coords[i].x; + } + if (coords[i].y < minY) { + minY = coords[i].y; + } else if (coords[i].y > maxY) { + maxY = coords[i].y; + } + } + return [minX, minY, maxX - minX, maxY - minY]; +} + +function sortTriangles(centerLatitudes: number[], indices: TriangleIndexArray): [number[], TriangleIndexArray] { + const triangleCount = centerLatitudes.length; + assert(indices.length === triangleCount); + + // Sorting triangles + const triangleIndexes = Array.from({length: triangleCount}, (v, i) => i); + + triangleIndexes.sort((idx1: number, idx2: number) => { + return centerLatitudes[idx1] - centerLatitudes[idx2]; + }); + + const sortedCenterLatitudes = []; + const sortedIndices = new TriangleIndexArray(); + + for (let i = 0; i < triangleIndexes.length; i++) { + const idx = triangleIndexes[i]; + sortedCenterLatitudes.push(centerLatitudes[idx]); + const i0 = idx * 3; + const i1 = i0 + 1; + const i2 = i1 + 1; + sortedIndices.emplaceBack(indices.uint16[i0], indices.uint16[i1], indices.uint16[i2]); + } + + return [sortedCenterLatitudes, sortedIndices]; +} + /** * A data source containing an image. * See the [Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/#sources-image) for detailed documentation of options. @@ -119,13 +239,21 @@ class ImageSource extends Evented implements Source { tileID: ?CanonicalTileID; onNorthPole: boolean; onSouthPole: boolean; + _unsupportedCoords: boolean; _boundsArray: ?RasterBoundsArray; boundsBuffer: ?VertexBuffer; boundsSegments: ?SegmentVector; + elevatedGlobeVertexBuffer: ?VertexBuffer; + elevatedGlobeIndexBuffer: ?IndexBuffer; + elevatedGlobeSegments: ?SegmentVector; + elevatedGlobeTrianglesCenterLongitudes: ?number[]; + maxLongitudeTriangleSize: number; + elevatedGlobeGridMatrix: ?Float32Array; _loaded: boolean; _dirty: boolean; _imageRequest: ?Cancelable; perspectiveTransform: [number, number]; + elevatedGlobePerspectiveTransform: [number, number]; /** * @private @@ -171,7 +299,6 @@ class ImageSource extends Evented implements Source { if (err) { this.fire(new ErrorEvent(err)); } else if (image) { - const {HTMLImageElement} = window; if (image instanceof HTMLImageElement) { this.image = browser.getImageData(image); } else { @@ -199,9 +326,9 @@ class ImageSource extends Evented implements Source { * @param {Object} options Options object. * @param {string} [options.url] Required image URL. * @param {Array>} [options.coordinates] Four geographical coordinates, - * represented as arrays of longitude and latitude numbers, which define the corners of the image. - * The coordinates start at the top left corner of the image and proceed in clockwise order. - * They do not have to represent a rectangle. + * represented as arrays of longitude and latitude numbers, which define the corners of the image. + * The coordinates start at the top left corner of the image and proceed in clockwise order. + * They do not have to represent a rectangle. * @returns {ImageSource} Returns itself to allow for method chaining. * @example * // Add to an image source to the map with some initial URL and coordinates @@ -273,15 +400,24 @@ class ImageSource extends Evented implements Source { this._imageRequest = null; } if (this.texture && !(this.texture instanceof UserManagedTexture)) this.texture.destroy(); + if (this.boundsBuffer) { + this.boundsBuffer.destroy(); + if (this.elevatedGlobeVertexBuffer) { + this.elevatedGlobeVertexBuffer.destroy(); + } + if (this.elevatedGlobeIndexBuffer) { + this.elevatedGlobeIndexBuffer.destroy(); + } + } } /** * Sets the image's coordinates and re-renders the map. * * @param {Array>} coordinates Four geographical coordinates, - * represented as arrays of longitude and latitude numbers, which define the corners of the image. - * The coordinates start at the top left corner of the image and proceed in clockwise order. - * They do not have to represent a rectangle. + * represented as arrays of longitude and latitude numbers, which define the corners of the image. + * The coordinates start at the top left corner of the image and proceed in clockwise order. + * They do not have to represent a rectangle. * @returns {ImageSource} Returns itself to allow for method chaining. * @example * // Add an image source to the map with some initial coordinates @@ -306,6 +442,7 @@ class ImageSource extends Evented implements Source { setCoordinates(coordinates: Coordinates): this { this.coordinates = coordinates; this._boundsArray = undefined; + this._unsupportedCoords = false; if (!coordinates.length) { assert(false); @@ -356,6 +493,7 @@ class ImageSource extends Evented implements Source { // $FlowFixMe[method-unbinding] _clear() { this._boundsArray = undefined; + this._unsupportedCoords = false; } _prepareData(context: Context) { @@ -367,7 +505,22 @@ class ImageSource extends Evented implements Source { } } - if (this._boundsArray) return; + if (this._boundsArray || this.onNorthPole || this.onSouthPole || this._unsupportedCoords) return; + + const globalTileTr = tileTransform(new CanonicalTileID(0, 0, 0), this.map.transform.projection); + + const globalTileCoords = [ + globalTileTr.projection.project(this.coordinates[0][0], this.coordinates[0][1]), + globalTileTr.projection.project(this.coordinates[1][0], this.coordinates[1][1]), + globalTileTr.projection.project(this.coordinates[2][0], this.coordinates[2][1]), + globalTileTr.projection.project(this.coordinates[3][0], this.coordinates[3][1]) + ]; + + if (!isConvex(globalTileCoords)) { + console.warn('Image source coordinates are defining non-convex area in the Mercator projection'); + this._unsupportedCoords = true; + return; + } const tileTr = tileTransform(this.tileID, this.map.transform.projection); @@ -377,8 +530,7 @@ class ImageSource extends Evented implements Source { return getTilePoint(tileTr, projectedCoord)._round(); }); - this.perspectiveTransform = getPerspectiveTransform( - this.width, this.height, tl.x, tl.y, tr.x, tr.y, bl.x, bl.y, br.x, br.y); + this.perspectiveTransform = getPerspectiveTransform(tl.x, tl.y, tr.x, tr.y, br.x, br.y, bl.x, bl.y); const boundsArray = this._boundsArray = new RasterBoundsArray(); boundsArray.emplaceBack(tl.x, tl.y, 0, 0); @@ -388,9 +540,139 @@ class ImageSource extends Evented implements Source { if (this.boundsBuffer) { this.boundsBuffer.destroy(); + if (this.elevatedGlobeVertexBuffer) { + this.elevatedGlobeVertexBuffer.destroy(); + } + if (this.elevatedGlobeIndexBuffer) { + this.elevatedGlobeIndexBuffer.destroy(); + } } this.boundsBuffer = context.createVertexBuffer(boundsArray, boundsAttributes.members); this.boundsSegments = SegmentVector.simpleSegment(0, 0, 4, 2); + + // Creating a mesh for elevated rasters in the globe projection. + // We want to follow the curve of the globe, but on the same time we can't use and transform + // grid buffers from globeSharedBuffers for several reasons: + // * our mesh in the Mercator projection is non-rectangular (for example, can be rotated), + // and the latitude has non-linear dependency from tile y coordinate, so we can't restore + // lat/lon just by multiplying a grid matrix and a vertex; + // * it has limited precision (neighbour points differ only by 1); + // * we also want to store UV coordinates as attributes. + // Grid coordinates go from 0 to EXTENT and contain transformed longitude/latitude, + // but the grid itself is linear in tile cooridinates, cause we want to get just the same result as with + // draped rasters. + // We calculate UV using matrix for perspective projection for all vertices and also correct UV interpolation + // inside a triangle in shader. + // During a transition from the Globe projection to the Mercator projection some triangles becomes stretched. + // In order to detect and skip these triangles we sort them by their middle longitude. + // We also calculate the maximum longitude size for triangles, and during rendering we find which triangles + // are close to the vertical line on the other side of the Globe and skip them - during rendering we can have + // two draw calls instead of one (draw all before the gap and after) or just one (dropped triangles are at + // the beginning and at the end of our array). + + const cellCount = GLOBE_VERTEX_GRID_SIZE; + const lineSize = cellCount + 1; + const linesCount = cellCount + 1; + const vertexCount = lineSize * linesCount; + const triangleCount = cellCount * cellCount * 2; + const verticesLongitudes = []; + const constrainedCoordinates = constrain(this.coordinates); + const [minLng, minLat, lngDiff, latDiff] = calculateMinAndSize(constrainedCoordinates); + + // Vertices + { + const elevatedGlobeVertexArray = new RasterBoundsArray(); + + const [minX, minY, dx, dy] = calculateMinAndSizeForPoints(globalTileCoords); + + const transformToImagePoint = (coord: ProjectedPoint) => { + return [(coord.x - minX) / dx, (coord.y - minY) / dy]; + }; + const [p0, p1, p2, p3] = globalTileCoords.map(transformToImagePoint); + const toUV = getTileToTextureTransformMatrix(p0[0], p0[1], p1[0], p1[1], p2[0], p2[1], p3[0], p3[1]); + this.elevatedGlobePerspectiveTransform = getPerspectiveTransform(p0[0], p0[1], p1[0], p1[1], p2[0], p2[1], p3[0], p3[1]); + + const addVertex = (point: LngLat, tilePoint: ProjectedPoint) => { + verticesLongitudes.push(point.lng); + const x = Math.round((point.lng - minLng) / lngDiff * EXTENT); + const y = Math.round((point.lat - minLat) / latDiff * EXTENT); + const imagePoint = transformToImagePoint(tilePoint); + const uv = vec3.transformMat3([], [imagePoint[0], imagePoint[1], 1], toUV); + const u = Math.round(uv[0] / uv[2] * EXTENT); + const v = Math.round(uv[1] / uv[2] * EXTENT); + elevatedGlobeVertexArray.emplaceBack(x, y, u, v); + }; + + const leftDx = globalTileCoords[3].x - globalTileCoords[0].x; + const leftDy = globalTileCoords[3].y - globalTileCoords[0].y; + const rightDx = globalTileCoords[2].x - globalTileCoords[1].x; + const rightDy = globalTileCoords[2].y - globalTileCoords[1].y; + + for (let i = 0; i < linesCount; i++) { + const linesPart = i / cellCount; + const startLinePoint = [globalTileCoords[0].x + linesPart * leftDx, globalTileCoords[0].y + linesPart * leftDy]; + const endLinePoint = [globalTileCoords[1].x + linesPart * rightDx, globalTileCoords[1].y + linesPart * rightDy]; + const lineDx = endLinePoint[0] - startLinePoint[0]; + const lineDy = endLinePoint[1] - startLinePoint[1]; + + for (let j = 0; j < lineSize; j++) { + const linePart = j / cellCount; + const point = {x: startLinePoint[0] + lineDx * linePart, y: startLinePoint[1] + lineDy * linePart, z: 0}; + addVertex(globalTileTr.projection.unproject(point.x, point.y), point); + } + } + + this.elevatedGlobeVertexBuffer = context.createVertexBuffer(elevatedGlobeVertexArray, boundsAttributes.members); + } + + // Indices + { + this.maxLongitudeTriangleSize = 0; + let elevatedGlobeTrianglesCenterLongitudes = []; + + let indices = new TriangleIndexArray(); + + const processTriangle = (i0: number, i1: number, i2: number) => { + indices.emplaceBack(i0, i1, i2); + + const l0 = verticesLongitudes[i0]; + const l1 = verticesLongitudes[i1]; + const l2 = verticesLongitudes[i2]; + const minLongitude = Math.min(Math.min(l0, l1), l2); + const maxLongitude = Math.max(Math.max(l0, l1), l2); + const diff = maxLongitude - minLongitude; + if (diff > this.maxLongitudeTriangleSize) { + this.maxLongitudeTriangleSize = diff; + } + elevatedGlobeTrianglesCenterLongitudes.push(minLongitude + diff / 2.); + }; + + for (let i = 0; i < cellCount; i++) { + for (let j = 0; j < cellCount; j++) { + // Making indexes the way that after transforming to the Globe projection triangles + // on our side will be rotated clockwise. + // lon + // ^ + // | 2 3 + // | 0 1 + // +------> lat + const i0 = i * lineSize + j; + const i1 = i0 + 1; + const i2 = i0 + lineSize; + const i3 = i2 + 1; + processTriangle(i0, i2, i1); + processTriangle(i1, i2, i3); + } + } + + [elevatedGlobeTrianglesCenterLongitudes, indices] = sortTriangles(elevatedGlobeTrianglesCenterLongitudes, indices); + + this.elevatedGlobeTrianglesCenterLongitudes = elevatedGlobeTrianglesCenterLongitudes; + this.elevatedGlobeIndexBuffer = context.createIndexBuffer(indices); + } + + this.elevatedGlobeSegments = SegmentVector.simpleSegment(0, 0, vertexCount, triangleCount); + this.elevatedGlobeGridMatrix = new Float32Array([0, lngDiff / EXTENT, 0, latDiff / EXTENT, 0, 0, minLat, minLng, 0]); } // $FlowFixMe[method-unbinding] @@ -443,6 +725,74 @@ class ImageSource extends Evented implements Source { hasTransition(): boolean { return false; } + + getSegmentsForLongitude(longitude: number): ?SegmentVector { + const segments = this.elevatedGlobeSegments; + if (!this.elevatedGlobeTrianglesCenterLongitudes || !segments) { + return null; + } + const longitudes = this.elevatedGlobeTrianglesCenterLongitudes; + assert(longitudes.length !== 0); + + // Normalizing longitude so that abs(normalizedLongitude - desiredLongitude) <= 180 + const normalizeLongitudeTo = (longitude: number, desiredLongitude: number) => { + const diff = Math.round((desiredLongitude - longitude) / 360.); + return longitude + diff * 360.; + }; + + let gapLongitude = normalizeLongitudeTo(longitude + 180., longitudes[0]); + const ret = new SegmentVector(); + + const addTriangleRange = (triangleOffset: number, triangleCount: number) => { + ret.segments.push( + { + vertexOffset: 0, + primitiveOffset: triangleOffset, + vertexLength: segments.segments[0].vertexLength, + primitiveLength: triangleCount, + sortKey: undefined, + vaos: {} + }); + }; + + // +0.01 - just to be sure that we don't draw "bad" triangles because of calculation errors + const distanceToDrop = 0.51 * this.maxLongitudeTriangleSize; + assert(distanceToDrop > 0); + assert(distanceToDrop < 180.); + + if (Math.abs(longitudes[0] - gapLongitude) <= distanceToDrop) { + const minIdx = upperBound(longitudes, 0, longitudes.length, gapLongitude + distanceToDrop); + if (minIdx === longitudes.length) { + // Rotated 90 degrees, and one side is almost zero? + return ret; + } + const maxIdx = lowerBound(longitudes, minIdx + 1, longitudes.length, gapLongitude + 360. - distanceToDrop); + const count = maxIdx - minIdx; + addTriangleRange(minIdx, count); + return ret; + } + + if (gapLongitude < longitudes[0]) { + gapLongitude += 360.; + } + + // Looking for the range inside or in the end of our triangles array to skip + const minIdx = lowerBound(longitudes, 0, longitudes.length, gapLongitude - distanceToDrop); + if (minIdx === longitudes.length) { + // Skip nothing + addTriangleRange(0, longitudes.length); + return ret; + } + + addTriangleRange(0, minIdx - 0); + + const maxIdx = upperBound(longitudes, minIdx + 1, longitudes.length, gapLongitude + distanceToDrop); + if (maxIdx !== longitudes.length) { + addTriangleRange(maxIdx, longitudes.length - maxIdx); + } + + return ret; + } } /** @@ -470,9 +820,14 @@ export function getCoordinatesCenterTileID(coords: Array): C const zoom = Math.max(0, Math.floor(-Math.log(dMax) / Math.LN2)); const tilesAtZoom = Math.pow(2, zoom); + let x = Math.floor((minX + maxX) / 2 * tilesAtZoom); + if (x > 1) { + x -= 1; + } + return new CanonicalTileID( zoom, - Math.floor((minX + maxX) / 2 * tilesAtZoom), + x, Math.floor((minY + maxY) / 2 * tilesAtZoom)); } diff --git a/src/source/load_tilejson.js b/src/source/load_tilejson.js index dd32a097066..c3c0e68e9af 100644 --- a/src/source/load_tilejson.js +++ b/src/source/load_tilejson.js @@ -9,16 +9,24 @@ import type {RequestManager} from '../util/mapbox.js'; import type {Callback} from '../types/callback.js'; import type {TileJSON} from '../types/tilejson.js'; import type {Cancelable} from '../types/cancelable.js'; +import type {SourceVectorLayer, SourceRasterLayer} from './source.js'; -export default function(options: any, requestManager: RequestManager, language: ?string, worldview: ?string, callback: Callback): Cancelable { - const loaded = function(err: ?Error, tileJSON: ?Object) { +type ExtendedTileJSON = TileJSON & { + vectorLayers?: Array; + vectorLayerIds?: Array; + rasterLayers?: Array; + rasterLayerIds?: Array; +}; + +export default function(options: any, requestManager: RequestManager, language: ?string, worldview: ?string, callback: Callback): Cancelable { + const loaded = function(err: ?Error, tileJSON: ?TileJSON) { if (err) { return callback(err); } else if (tileJSON) { // Prefer TileJSON tiles, if both URL and tiles options are set if (options.url && tileJSON.tiles && options.tiles) delete options.tiles; - const result: any = pick( + const result: ExtendedTileJSON = pick( // explicit source options take precedence over TileJSON extend(tileJSON, options), ['tiles', 'minzoom', 'maxzoom', 'attribution', 'mapbox_logo', 'bounds', 'scheme', 'tileSize', 'encoding'] @@ -29,6 +37,11 @@ export default function(options: any, requestManager: RequestManager, language: result.vectorLayerIds = result.vectorLayers.map((layer) => { return layer.id; }); } + if (tileJSON.raster_layers) { + result.rasterLayers = tileJSON.raster_layers; + result.rasterLayerIds = result.rasterLayers.map((layer) => { return layer.id; }); + } + result.tiles = requestManager.canonicalizeTileset(result, options.url); callback(null, result); } diff --git a/src/source/load_vector_tile.js b/src/source/load_vector_tile.js new file mode 100644 index 00000000000..4c0974f0070 --- /dev/null +++ b/src/source/load_vector_tile.js @@ -0,0 +1,116 @@ +// @flow + +import {VectorTile} from '@mapbox/vector-tile'; +import Protobuf from 'pbf'; +import {getArrayBuffer} from '../util/ajax.js'; + +import type {IVectorTile} from '@mapbox/vector-tile'; +import type {Callback} from '../types/callback.js'; +import type {RequestedTileParameters} from './worker_source.js'; +import type Scheduler from '../util/scheduler.js'; + +export type LoadVectorTileResult = { + rawData: ArrayBuffer; + vectorTile?: IVectorTile; + expires?: any; + cacheControl?: any; + resourceTiming?: Array; +}; + +/** + * @callback LoadVectorDataCallback + * @param error + * @param vectorTile + * @private + */ +export type LoadVectorDataCallback = Callback; + +export type AbortVectorData = () => void; +export type LoadVectorData = (params: RequestedTileParameters, callback: LoadVectorDataCallback) => ?AbortVectorData; +export class DedupedRequest { + entries: { [string]: Object }; + scheduler: ?Scheduler; + + constructor(scheduler?: Scheduler) { + this.entries = {}; + this.scheduler = scheduler; + } + + request(key: string, metadata: Object, request: any, callback: LoadVectorDataCallback): (() => void) { + const entry = this.entries[key] = this.entries[key] || {callbacks: []}; + + if (entry.result) { + const [err, result] = entry.result; + if (this.scheduler) { + this.scheduler.add(() => { + callback(err, result); + }, metadata); + } else { + callback(err, result); + } + return () => {}; + } + + entry.callbacks.push(callback); + + if (!entry.cancel) { + entry.cancel = request((err, result) => { + entry.result = [err, result]; + for (const cb of entry.callbacks) { + if (this.scheduler) { + this.scheduler.add(() => { + cb(err, result); + }, metadata); + } else { + cb(err, result); + } + } + setTimeout(() => delete this.entries[key], 1000 * 3); + }); + } + + return () => { + if (entry.result) return; + entry.callbacks = entry.callbacks.filter(cb => cb !== callback); + if (!entry.callbacks.length) { + entry.cancel(); + delete this.entries[key]; + } + }; + } +} + +/** + * @private + */ +// $FlowFixMe[missing-this-annot] +export function loadVectorTile(params: RequestedTileParameters, callback: LoadVectorDataCallback, skipParse?: boolean): (() => void) { + const key = JSON.stringify(params.request); + + const makeRequest = (callback: LoadVectorDataCallback) => { + const request = getArrayBuffer(params.request, (err: ?Error, data: ?ArrayBuffer, cacheControl: ?string, expires: ?string) => { + if (err) { + callback(err); + } else if (data) { + callback(null, { + vectorTile: skipParse ? undefined : new VectorTile(new Protobuf(data)), + rawData: data, + cacheControl, + expires + }); + } + }); + return () => { + request.cancel(); + callback(); + }; + }; + + if (params.data) { + // if we already got the result earlier (on the main thread), return it directly + (this.deduped: DedupedRequest).entries[key] = {result: [null, params.data]}; + } + + const callbackMetadata = {type: 'parseTile', isSymbolTile: params.isSymbolTile, zoom: params.tileZoom}; + return (this.deduped: DedupedRequest).request(key, callbackMetadata, makeRequest, callback); +} diff --git a/src/source/raster_array_tile.js b/src/source/raster_array_tile.js new file mode 100644 index 00000000000..f89e4ae3874 --- /dev/null +++ b/src/source/raster_array_tile.js @@ -0,0 +1,310 @@ +// @flow + +import Tile from './tile.js'; +import Texture from '../render/texture.js'; +import {getArrayBuffer} from '../util/ajax.js'; +import {MapboxRasterTile} from '../data/mrt/mrt.js'; + +import type Painter from '../render/painter.js'; +import type Framebuffer from '../gl/framebuffer.js'; +import type {Callback} from '../types/callback.js'; +import type {Cancelable} from '../types/cancelable.js'; +import type {TextureImage} from '../render/texture.js'; +import type {OverscaledTileID} from './tile_id.js'; +import type {RequestParameters, ResponseCallback} from '../util/ajax.js'; + +export type TextureDescriptor = { + img: TextureImage, + layer: string, + band: string | number, + tileSize: number, + buffer: number, + mix: [number, number, number, number], + offset: number, + format?: 'uint8' | 'uint16' | 'uint32', +}; + +export type MRTLayer = { + version: number; + name: string; + units: string; + tilesize: number; + buffer: number; + pixelFormat: 'uint8' | 'uint16' | 'uint32'; + dataIndex: {[string | number]: any}; + hasBand: (string | number) => boolean; + hasDataForBand: (string | number) => boolean; + getDataRange: (Array) => MRTDataRange; + getBandView: (string | number) => MRTBandView; +} + +export type MRTBandView = { + data: any, + bytes: any, + tileSize: number, + buffer: number, + offset: number, + scale: number, +}; + +export type MRTDataRange = { + layerName: string; + firstByte: number; + lastByte: number; + firstBlock: number; + lastBlock: number; +}; + +export type MRTDecodingBatch = { + tasks: Array; + cancel: () => void; + complete: (?Error, ?ArrayBuffer) => void; +}; + +export type MRTDecodingTask = { + layerName: string; + firstByte: number; + lastByte: number; + pixelFormat: 'uint8' | 'uint16' | 'uint32'; + blockIndex: number; + blockShape: Array; + buffer: number; + codec: string; + filters: Array; +}; + +export type MRT = { + x: number; + y: number; + z: number; + _cacheSize: number; + + layers: {[_: string]: MRTLayer}; + getLayer(string): ?MRTLayer; + parseHeader(ArrayBuffer): MRT; + getHeaderLength(ArrayBuffer): number; + createDecodingTask(MRTDataRange): MRTDecodingBatch; +}; + +const FIRST_TRY_HEADER_LENGTH = 16384; +const MRT_DECODED_BAND_CACHE_SIZE = 30; + +class RasterArrayTile extends Tile { + // $FlowFixMe[incompatible-extend] + texture: ?Texture; + entireBuffer: ?ArrayBuffer; + requestParams: ?RequestParameters; + + _workQueue: Array<() => void>; + _fetchQueue: Array<() => void>; + + fbo: ?Framebuffer; + textureDescriptor: ?TextureDescriptor; + + _mrt: ?MRT; + _isHeaderLoaded: boolean; + + constructor(tileID: OverscaledTileID, size: number, tileZoom: number, painter: ?Painter, isRaster?: boolean) { + super(tileID, size, tileZoom, painter, isRaster); + + this._workQueue = []; + this._fetchQueue = []; + this._isHeaderLoaded = false; + } + + setTexture(img: TextureImage, painter: Painter) { + const context = painter.context; + const gl = context.gl; + this.texture = this.texture || painter.getTileTexture(img.width); + + if (this.texture && this.texture instanceof Texture) { + this.texture.update(img, {useMipmap: false, premultiply: false}); + } else { + this.texture = new Texture(context, img, gl.RGBA, {useMipmap: false, premultiply: false}); + } + } + + /** + * Stops existing fetches + * @private + */ + flushQueues() { + while (this._workQueue.length) { + (this._workQueue.pop())(); + } + + while (this._fetchQueue.length) { + (this._fetchQueue.pop())(); + } + } + + fetchHeader(fetchLength: number = FIRST_TRY_HEADER_LENGTH, callback: ResponseCallback): Cancelable { + const mrt = this._mrt = new MapboxRasterTile(MRT_DECODED_BAND_CACHE_SIZE); + + const headerRequestParams = Object.assign({}, this.requestParams, {headers: {Range: `bytes=0-${fetchLength - 1}`}}); + + // A buffer, in case range requests were ignored + this.entireBuffer = null; + + this.request = getArrayBuffer(headerRequestParams, (error: ?Error, dataBuffer: ?ArrayBuffer, cacheControl: ?string, expires: ?string) => { + if (error) { + callback(error); + return; + } + + try { + const headerLength = mrt.getHeaderLength(dataBuffer); + if (headerLength > fetchLength) { + this.request = this.fetchHeader(headerLength, callback); + return; + } + + // Parse the header only + mrt.parseHeader(dataBuffer); + this._isHeaderLoaded = true; + + // If the received data covers all possible byte ranges (i.e. if the range request was + // ignored by the server), then cache the buffer and neglect range requests. + let lastByte = 0; + for (const layer of Object.values(mrt.layers)) { + // $FlowFixMe[incompatible-use] + lastByte = Math.max(lastByte, layer.dataIndex[layer.dataIndex.length - 1].last_byte); + } + + // $FlowFixMe[incompatible-use] + if (dataBuffer.byteLength >= lastByte) { + this.entireBuffer = dataBuffer; + } + + callback(null, (this.entireBuffer || dataBuffer), cacheControl, expires); + } catch (error) { + callback(error); + } + }); + + return this.request; + } + + fetchBand(sourceLayer: string, band: string | number, callback: Callback) { + // If header is not loaded, bail out of rendering. + // Repaint on reload is handled by appropriate callbacks. + const mrt = this._mrt; + if (!this._isHeaderLoaded || !mrt) { + callback(new Error('Tile header is not ready')); + return; + } + + const actor = this.actor; + if (!actor) { + callback(new Error('Can\'t fetch tile band without an actor')); + return; + } + + // eslint-disable-next-line prefer-const + let task; + + const onDataDecoded = (err: ?Error, result: ?ArrayBuffer) => { + task.complete(err, result); + if (err) { + callback(err); + return; + } + + this.updateTextureDescriptor(sourceLayer, band); + callback(null, this.textureDescriptor && this.textureDescriptor.img); + }; + + const onDataLoaded = (err: ?Error, buffer: ?ArrayBuffer) => { + if (err) return callback(err); + + const params = {buffer, task}; + const workerJob = actor.send('decodeRasterArray', params, onDataDecoded, undefined, true); + + this._workQueue.push(() => { + if (workerJob) workerJob.cancel(); + task.cancel(); + }); + }; + + const mrtLayer = mrt.getLayer(sourceLayer); + if (!mrtLayer) { + callback(new Error(`Unknown sourceLayer "${sourceLayer}"`)); + return; + } + + if (mrtLayer.hasDataForBand(band)) { + this.updateTextureDescriptor(sourceLayer, band); + callback(null, this.textureDescriptor ? this.textureDescriptor.img : null); + return; + } + + const range = mrtLayer.getDataRange([band]); + task = mrt.createDecodingTask(range); + + // The MRT instance will not return work for a task which has already been checked + // out but not completed. If the resulting task has no work, we presume it is in + // progress. (This makes it very important to correctly cancel aborted decoding tasks.) + if (task && !task.tasks.length) { + callback(null); + return; + } + + // Stop existing fetches and decodes + this.flushQueues(); + + if (this.entireBuffer) { + // eslint-disable-next-line no-warning-comments + // TODO: can we decode without slicing and duplicating memory? + onDataLoaded(null, this.entireBuffer.slice(range.firstByte, range.lastByte + 1)); + } else { + const rangeRequestParams = Object.assign({}, this.requestParams, {headers: {Range: `bytes=${range.firstByte}-${range.lastByte}`}}); + const request = getArrayBuffer(rangeRequestParams, onDataLoaded); + this._fetchQueue.push(() => { + request.cancel(); + task.cancel(); + }); + } + } + + updateNeeded(sourceLayer: string, band: string | number): boolean { + const textureUpdateNeeded = !this.textureDescriptor || + this.textureDescriptor.band !== band || + this.textureDescriptor.layer !== sourceLayer; + + return textureUpdateNeeded && this.state !== 'errored'; + } + + updateTextureDescriptor(sourceLayer: string, band: string | number): void { + if (!this._mrt) return; + + const mrtLayer = this._mrt.getLayer(sourceLayer); + if (!mrtLayer || !mrtLayer.hasBand(band) || !mrtLayer.hasDataForBand(band)) return; + + const {bytes, tileSize, buffer, offset, scale} = mrtLayer.getBandView(band); + const size = tileSize + 2 * buffer; + const img = {data: bytes, width: size, height: size}; + + const texture = this.texture; + if (texture && texture instanceof Texture) { + texture.update(img, {useMipmap: false, premultiply: false}); + } + + this.textureDescriptor = { + layer: sourceLayer, + band, + img, + buffer, + offset, + tileSize, + format: mrtLayer.pixelFormat, + mix: [ + scale, + scale * 256, + scale * 65536, + scale * 16777216, + ] + }; + } +} + +export default RasterArrayTile; diff --git a/src/source/raster_array_tile_source.js b/src/source/raster_array_tile_source.js new file mode 100644 index 00000000000..9e08f04f45c --- /dev/null +++ b/src/source/raster_array_tile_source.js @@ -0,0 +1,190 @@ +// @flow + +import Texture from '../render/texture.js'; +import RasterTileSource from './raster_tile_source.js'; +import {extend} from '../util/util.js'; +import {ResourceType} from '../util/ajax.js'; +import {Evented, ErrorEvent} from '../util/evented.js'; +import RasterStyleLayer from '../style/style_layer/raster_style_layer.js'; +import RasterParticleStyleLayer from '../style/style_layer/raster_particle_style_layer.js'; + +// Import MRTData as a module with side effects to ensure +// it's registered as a serializable class on the main thread +import '../data/mrt_data.js'; + +import type {Map} from '../ui/map.js'; +import type Dispatcher from '../util/dispatcher.js'; +import type RasterArrayTile from './raster_array_tile.js'; +import type {Callback} from '../types/callback.js'; +import type {TextureDescriptor} from './raster_array_tile.js'; +import type {Source, SourceRasterLayer} from './source.js'; +import type {RasterArraySourceSpecification} from '../style-spec/types.js'; + +// $FlowFixMe[method-unbinding] +class RasterArrayTileSource extends RasterTileSource implements Source { + map: Map; + rasterLayers: Array | void; + rasterLayerIds: Array | void; + + constructor(id: string, options: RasterArraySourceSpecification, dispatcher: Dispatcher, eventedParent: Evented) { + super(id, options, dispatcher, eventedParent); + this.type = 'raster-array'; + this.maxzoom = 22; + this._options = extend({type: 'raster-array'}, options); + } + + triggerRepaint(tile: RasterArrayTile) { + const terrain = this.map.painter._terrain; + const sourceCache = this.map.style.getSourceCache(this.id); + if (terrain && terrain.enabled && sourceCache) { + terrain._clearRenderCacheForTile(sourceCache.id, tile.tileID); + } + + // eslint-disable-next-line no-warning-comments + // TODO: trigger repaint only if all tiles have the requested band + this.map.triggerRepaint(); + } + + // $FlowFixMe[incompatible-type] + // $FlowFixMe[incompatible-extend] + loadTile(tile: RasterArrayTile, callback: Callback) { + const url = this.map._requestManager.normalizeTileURL(tile.tileID.canonical.url(this.tiles, this.scheme), false, this.tileSize); + const requestParams = this.map._requestManager.transformRequest(url, ResourceType.Tile); + + tile.requestParams = requestParams; + if (!tile.actor) tile.actor = this.dispatcher.getActor(); + + tile.request = tile.fetchHeader(undefined, (error: ?Error, dataBuffer: ?ArrayBuffer, cacheControl: ?string, expires: ?string) => { + delete tile.request; + + if (tile.aborted) { + tile.state = 'unloaded'; + return callback(null); + } + + if (error) { + // silence AbortError + // $FlowFixMe[prop-missing] + if (error.code === 20) return; + tile.state = 'errored'; + return callback(error); + } + + if (this.map._refreshExpiredTiles) tile.setExpiryData({cacheControl, expires}); + + tile.state = 'empty'; + callback(null); + }); + } + + // $FlowFixMe[method-unbinding] + // $FlowFixMe[incompatible-type] + // $FlowFixMe[incompatible-extend] + unloadTile(tile: RasterArrayTile) { + const texture = tile.texture; + if (texture && texture instanceof Texture) { + // Clean everything else up owned by the tile, but preserve the texture. + // Destroy first to prevent racing with the texture cache being popped. + tile.destroy(true); + + // Save the texture to the cache + this.map.painter.saveTileTexture(texture); + } else { + tile.destroy(); + + tile.flushQueues(); + tile._isHeaderLoaded = false; + + delete tile._mrt; + delete tile.textureDescriptor; + } + + if (tile.fbo) { + tile.fbo.destroy(); + delete tile.fbo; + } + + delete tile.request; + delete tile.requestParams; + + delete tile.neighboringTiles; + tile.state = 'unloaded'; + } + + /** + * Prepare RasterArrayTile for the rendering. If tile doesn't have data + * for the requested band, fetch and repaint once it's acquired. + * @private + */ + prepareTile(tile: RasterArrayTile, sourceLayer: string, band: string | number) { + // Skip if tile is not yet loaded or if no update is needed + if (!tile._isHeaderLoaded) return; + + // Don't mark tile as reloading if it was empty. + if (tile.state !== 'empty') tile.state = 'reloading'; + + // Fetch data for band and then repaint once data is acquired. + tile.fetchBand(sourceLayer, band, (error, data) => { + if (error) { + tile.state = 'errored'; + this.fire(new ErrorEvent(error)); + this.triggerRepaint(tile); + return; + } + + if (data) { + tile.setTexture(data, this.map.painter); + tile.state = 'loaded'; + this.triggerRepaint(tile); + } + }); + } + + /** + * Get the initial band for a source layer. + * @private + */ + getInitialBand(sourceLayer: string): string | number | void { + if (!this.rasterLayers) return 0; + const rasterLayer = this.rasterLayers.find(({id}) => id === sourceLayer); + const fields = rasterLayer && rasterLayer.fields; + const bands = fields && fields.bands && fields.bands; + return bands ? bands[0] : 0; + } + + /** + * Get a texture descriptor for a source layer and a band. + * @private + * @param {RasterArrayTile} tile + * @param {RasterStyleLayer} layer + * @param {boolean} fallbackToPrevious If true, return previous texture even if update is needed + * @returns {TextureDescriptor} Texture descriptor with texture if available + */ + getTextureDescriptor(tile: RasterArrayTile, layer: RasterStyleLayer | RasterParticleStyleLayer, fallbackToPrevious: boolean): TextureDescriptor & {texture: ?Texture} | void { + if (!tile) return; + + const sourceLayer = layer.sourceLayer || (this.rasterLayerIds && this.rasterLayerIds[0]); + if (!sourceLayer) return; + + let layerBand = null; + if (layer instanceof RasterStyleLayer) { + layerBand = layer.paint.get('raster-array-band'); + } else if (layer instanceof RasterParticleStyleLayer) { + layerBand = layer.paint.get('raster-particle-array-band'); + } + const band = layerBand || this.getInitialBand(sourceLayer); + if (band == null) return; + + if (!tile.textureDescriptor) { + this.prepareTile(tile, sourceLayer, band); + return; + } + + // Fallback to previous texture even if update is needed + if (tile.updateNeeded(sourceLayer, band) && !fallbackToPrevious) return; + + return Object.assign({}, tile.textureDescriptor, {texture: tile.texture}); + } +} + +export default RasterArrayTileSource; diff --git a/src/source/raster_array_tile_worker_source.js b/src/source/raster_array_tile_worker_source.js new file mode 100644 index 00000000000..339d052cb0f --- /dev/null +++ b/src/source/raster_array_tile_worker_source.js @@ -0,0 +1,22 @@ +// @flow + +import '../data/mrt_data.js'; +import {MapboxRasterTile} from '../data/mrt/mrt.js'; + +import type Actor from '../util/actor.js'; +import type {WorkerRasterArrayTileParameters, WorkerRasterArrayTileCallback} from './worker_source.js'; + +class RasterArrayTileWorkerSource { + actor: Actor; + + decodeRasterArray({task, buffer}: WorkerRasterArrayTileParameters, callback: WorkerRasterArrayTileCallback) { + MapboxRasterTile.performDecoding(buffer, task) + .then(result => { + callback(null, result); + }, error => { + callback(error); + }); + } +} + +export default RasterArrayTileWorkerSource; diff --git a/src/source/raster_dem_tile_source.js b/src/source/raster_dem_tile_source.js index 6b8e10842bf..7a017ec7594 100644 --- a/src/source/raster_dem_tile_source.js +++ b/src/source/raster_dem_tile_source.js @@ -4,13 +4,15 @@ import {getImage, ResourceType} from '../util/ajax.js'; import {extend, prevPowerOfTwo} from '../util/util.js'; import {Evented} from '../util/evented.js'; import browser from '../util/browser.js'; -import window from '../util/window.js'; import offscreenCanvasSupported from '../util/offscreen_canvas_supported.js'; import {OverscaledTileID} from './tile_id.js'; import RasterTileSource from './raster_tile_source.js'; -// ensure DEMData is registered for worker transfer on main thread: -import DEMData from '../data/dem_data.js'; +// Import DEMData as a module with side effects to ensure +// it's registered as a serializable class on the main thread +import '../data/dem_data.js'; + +import type DEMData from '../data/dem_data.js'; import type {Source} from './source.js'; import type Dispatcher from '../util/dispatcher.js'; import type Tile from './tile.js'; @@ -34,7 +36,6 @@ class RasterDEMTileSource extends RasterTileSource implements Source { const url = this.map._requestManager.normalizeTileURL(tile.tileID.canonical.url(this.tiles, this.scheme), false, this.tileSize); tile.request = getImage(this.map._requestManager.transformRequest(url, ResourceType.Tile), imageLoaded.bind(this)); - const convertDEMTexturesToFloatFormat = this.map?.painter?.terrainUseFloatDEM(); // $FlowFixMe[missing-this-annot] function imageLoaded(err: ?Error, img: ?TextureImage, cacheControl: ?string, expires: ?string) { delete tile.request; @@ -46,7 +47,7 @@ class RasterDEMTileSource extends RasterTileSource implements Source { callback(err); } else if (img) { if (this.map._refreshExpiredTiles) tile.setExpiryData({cacheControl, expires}); - const transfer = window.ImageBitmap && img instanceof window.ImageBitmap && offscreenCanvasSupported(); + const transfer = ImageBitmap && img instanceof ImageBitmap && offscreenCanvasSupported(); // DEMData uses 1px padding. Handle cases with image buffer of 1 and 2 pxs, the rest assume default buffer 0 // in order to keep the previous implementation working (no validation against tileSize). const buffer = (img.width - prevPowerOfTwo(img.width)) / 2; @@ -66,8 +67,7 @@ class RasterDEMTileSource extends RasterTileSource implements Source { scope: this.scope, rawImageData, encoding: this.encoding, - padding, - convertToFloat: convertDEMTexturesToFloatFormat + padding }; if (!tile.actor || tile.state === 'expired') { diff --git a/src/source/raster_dem_tile_worker_source.js b/src/source/raster_dem_tile_worker_source.js index 31914d831d0..5b6c822cc31 100644 --- a/src/source/raster_dem_tile_worker_source.js +++ b/src/source/raster_dem_tile_worker_source.js @@ -1,7 +1,6 @@ // @flow import DEMData from '../data/dem_data.js'; -import window from '../util/window.js'; import type Actor from '../util/actor.js'; import type {WorkerDEMTileParameters, WorkerDEMTileCallback} from './worker_source.js'; @@ -14,9 +13,9 @@ class RasterDEMTileWorkerSource { loadTile(params: WorkerDEMTileParameters, callback: WorkerDEMTileCallback) { const {uid, encoding, rawImageData, padding} = params; // Main thread will transfer ImageBitmap if offscreen decode with OffscreenCanvas is supported, else it will transfer an already decoded image. - // Flow struggles to refine ImageBitmap type, likely due to the JSDom shim - const imagePixels = window.ImageBitmap && rawImageData instanceof window.ImageBitmap ? this.getImageData(rawImageData, padding) : ((rawImageData: any): ImageData); - const dem = new DEMData(uid, imagePixels, encoding, params.convertToFloat, padding < 1); + // Flow struggles to refine ImageBitmap type + const imagePixels = ImageBitmap && rawImageData instanceof ImageBitmap ? this.getImageData(rawImageData, padding) : ((rawImageData: any): ImageData); + const dem = new DEMData(uid, imagePixels, encoding, padding < 1); callback(null, dem); } diff --git a/src/source/raster_tile_source.js b/src/source/raster_tile_source.js index a0262d01ea8..d07015a85c8 100644 --- a/src/source/raster_tile_source.js +++ b/src/source/raster_tile_source.js @@ -13,14 +13,15 @@ import {makeFQID} from '../util/fqid.js'; import type {Source} from './source.js'; import type {OverscaledTileID} from './tile_id.js'; -import type Map from '../ui/map.js'; +import type {Map} from '../ui/map.js'; import type Dispatcher from '../util/dispatcher.js'; import type Tile from './tile.js'; import type {Callback} from '../types/callback.js'; import type {Cancelable} from '../types/cancelable.js'; import type { RasterSourceSpecification, - RasterDEMSourceSpecification + RasterDEMSourceSpecification, + RasterArraySourceSpecification } from '../style-spec/types.js'; import Texture from '../render/texture.js'; @@ -46,7 +47,7 @@ import Texture from '../render/texture.js'; * @see [Example: Add a WMS source](https://docs.mapbox.com/mapbox-gl-js/example/wms/) */ class RasterTileSource extends Evented implements Source { - type: 'raster' | 'raster-dem'; + type: 'raster' | 'raster-dem' | 'raster-array'; id: string; scope: string; minzoom: number; @@ -63,10 +64,10 @@ class RasterTileSource extends Evented implements Source { tiles: Array; _loaded: boolean; - _options: RasterSourceSpecification | RasterDEMSourceSpecification; + _options: RasterSourceSpecification | RasterDEMSourceSpecification | RasterArraySourceSpecification; _tileJSONRequest: ?Cancelable; - constructor(id: string, options: RasterSourceSpecification | RasterDEMSourceSpecification, dispatcher: Dispatcher, eventedParent: Evented) { + constructor(id: string, options: RasterSourceSpecification | RasterDEMSourceSpecification | RasterArraySourceSpecification, dispatcher: Dispatcher, eventedParent: Evented) { super(); this.id = id; this.dispatcher = dispatcher; @@ -98,7 +99,7 @@ class RasterTileSource extends Evented implements Source { postTurnstileEvent(tileJSON.tiles); - // `content` is included here to prevent a race condition where `Style#_updateSources` is called + // `content` is included here to prevent a race condition where `Style#updateSources` is called // before the TileJSON arrives. this makes sure the tiles needed are loaded once TileJSON arrives // ref: https://github.com/mapbox/mapbox-gl-js/pull/4347#discussion_r104418088 this.fire(new Event('data', {dataType: 'source', sourceDataType: 'metadata'})); diff --git a/src/source/source.js b/src/source/source.js index b4c5337478d..198828bbe07 100644 --- a/src/source/source.js +++ b/src/source/source.js @@ -4,13 +4,31 @@ import {bindAll} from '../util/util.js'; import type Dispatcher from '../util/dispatcher.js'; import type {Event, Evented} from '../util/evented.js'; -import type Map from '../ui/map.js'; +import type {Map} from '../ui/map.js'; import type Tile from './tile.js'; import type {OverscaledTileID} from './tile_id.js'; import type {Callback} from '../types/callback.js'; import type {MapEvent} from '../ui/events.js'; import {CanonicalTileID} from './tile_id.js'; +import type {Class} from '../types/class.js'; + +export type SourceRasterLayer = { + id: string; + maxzoom?: number; + minzoom?: number; + fields?: { + bands?: Array; + range?: [number, number]; + }; +}; + +export type SourceVectorLayer = { + id: string; + maxzoom?: number; + minzoom?: number; +}; + /** * The `Source` interface must be implemented by each source type, including "core" types like `vector`, `raster`, * or `video`) and all custom, third-party types. @@ -50,13 +68,17 @@ export interface Source { mapbox_logo?: boolean, tileID?: CanonicalTileID; reparseOverscaled?: boolean, - vectorLayerIds?: Array, minTileCacheSize?: ?number; maxTileCacheSize?: ?number; language?: ?string; worldview?: ?string; +usedInConflation?: boolean; + vectorLayers?: Array; + vectorLayerIds?: Array; + rasterLayers?: Array; + rasterLayerIds?: Array; + hasTransition(): boolean; loaded(): boolean; @@ -101,6 +123,7 @@ export type SourceClass = Class & SourceStatics; import vector from '../source/vector_tile_source.js'; import raster from '../source/raster_tile_source.js'; import rasterDem from '../source/raster_dem_tile_source.js'; +import rasterArray from '../source/raster_array_tile_source.js'; import geojson from '../source/geojson_source.js'; import video from '../source/video_source.js'; import image from '../source/image_source.js'; @@ -114,6 +137,7 @@ const sourceTypes: {[string]: Class} = { vector, raster, 'raster-dem': rasterDem, + 'raster-array': rasterArray, geojson, video, image, @@ -135,7 +159,7 @@ const sourceTypes: {[string]: Class} = { */ export const create = function(id: string, specification: SourceSpecification, dispatcher: Dispatcher, eventedParent: Evented): Source { // $FlowFixMe[prop-missing] - const source = new sourceTypes[specification.type](id, (specification: any), dispatcher, eventedParent); + const source = new sourceTypes[specification.type](id, specification, dispatcher, eventedParent); if (source.id !== id) { throw new Error(`Expected Source id to be ${id} instead of ${source.id}`); diff --git a/src/source/source_cache.js b/src/source/source_cache.js index 9f7305c996e..39f42cee739 100644 --- a/src/source/source_cache.js +++ b/src/source/source_cache.js @@ -1,6 +1,7 @@ // @flow import Tile from './tile.js'; +import RasterArrayTile from './raster_array_tile.js'; import {Event, ErrorEvent, Evented} from '../util/evented.js'; import TileCache from './tile_cache.js'; import {asyncAll, keysDifference, values, clamp} from '../util/util.js'; @@ -14,7 +15,7 @@ import {mercatorXfromLng} from '../geo/mercator_coordinate.js'; import type {Source} from './source.js'; import type {SourceSpecification} from '../style-spec/types.js'; -import type {default as MapboxMap} from '../ui/map.js'; +import type {Map as MapboxMap} from '../ui/map.js'; import type Transform from '../geo/transform.js'; import type {TileState} from './tile.js'; import type {Callback} from '../types/callback.js'; @@ -55,6 +56,7 @@ class SourceCache extends Evented { used: boolean; usedForTerrain: boolean; castsShadows: boolean; + tileCoverLift: number; _state: SourceFeatureState; _loadedParentTiles: {[_: number | string]: ?Tile}; _onlySymbols: ?boolean; @@ -98,6 +100,7 @@ class SourceCache extends Evented { this._maxTileCacheSize = source.maxTileCacheSize; this._loadedParentTiles = {}; this.castsShadows = false; + this.tileCoverLift = 0.0; this._coveredTiles = {}; this._shadowCasterTiles = {}; @@ -105,6 +108,7 @@ class SourceCache extends Evented { this._isRaster = this._source.type === 'raster' || this._source.type === 'raster-dem' || + this._source.type === 'raster-array' || // $FlowFixMe[prop-missing] (this._source.type === 'custom' && this._source._dataType === 'raster'); } @@ -259,8 +263,19 @@ class SourceCache extends Evented { if (err) { tile.state = 'errored'; if ((err: any).status !== 404) this._source.fire(new ErrorEvent(err, {tile})); + // If the requested tile is missing, try to load the parent tile + // to use it as an overscaled tile instead of the missing one. else { - // continue to try loading parent/children tiles if a tile doesn't exist (404) + const hasParent = tile.tileID.key in this._loadedParentTiles; + // If there are no parent tiles to load, fire a `data` event to trigger map render + if (!hasParent) { + // We are firing an `error` source type event instead of `content` here because + // the `content` event will reload all tiles and trigger redundant source cache updates + this._source.fire(new Event('data', {dataType: 'source', sourceDataType: 'error', sourceId: this._source.id})); + return; + } + + // Otherwise, continue trying to load the parent tile until we find one that loads successfully const updateForTerrain = this._source.type === 'raster-dem' && this.usedForTerrain; if (updateForTerrain && this.map.painter.terrain) { const terrain = this.map.painter.terrain; @@ -523,6 +538,26 @@ class SourceCache extends Evented { } else if (this._source.tileID) { idealTileIDs = transform.getVisibleUnwrappedCoordinates(this._source.tileID) .map((unwrapped) => new OverscaledTileID(unwrapped.canonical.z, unwrapped.wrap, unwrapped.canonical.z, unwrapped.canonical.x, unwrapped.canonical.y)); + } else if (this.tileCoverLift !== 0.0) { + // Extended tile cover to load elevated tiles + const modifiedTransform = transform.clone(); + modifiedTransform.tileCoverLift = this.tileCoverLift; + idealTileIDs = modifiedTransform.coveringTiles({ + tileSize: tileSize || this._source.tileSize, + minzoom: this._source.minzoom, + maxzoom: this._source.maxzoom, + roundZoom: this._source.roundZoom && !updateForTerrain, + reparseOverscaled: this._source.reparseOverscaled, + isTerrainDEM: this.usedForTerrain + }); + + // Add zoom level 1 tiles to cover area behind globe + if (this._source.minzoom <= 1.0 && transform.projection.name === 'globe') { + idealTileIDs.push(new OverscaledTileID(1, 0, 1, 0, 0)); + idealTileIDs.push(new OverscaledTileID(1, 0, 1, 1, 0)); + idealTileIDs.push(new OverscaledTileID(1, 0, 1, 0, 1)); + idealTileIDs.push(new OverscaledTileID(1, 0, 1, 1, 1)); + } } else { idealTileIDs = transform.coveringTiles({ tileSize: tileSize || this._source.tileSize, @@ -797,7 +832,13 @@ class SourceCache extends Evented { const cached = Boolean(tile); if (!cached) { const painter = this.map ? this.map.painter : null; - tile = new Tile(tileID, this._source.tileSize * tileID.overscaleFactor(), this.transform.tileZoom, painter, this._isRaster); + const size = this._source.tileSize * tileID.overscaleFactor(); + const isRasterArray = this._source.type === 'raster-array'; + + tile = isRasterArray ? + new RasterArrayTile(tileID, size, this.transform.tileZoom, painter, this._isRaster) : + new Tile(tileID, size, this.transform.tileZoom, painter, this._isRaster); + // $FlowFixMe[method-unbinding] this._loadTile(tile, this._tileLoaded.bind(this, tile, tileID.key, tile.state)); } @@ -846,7 +887,7 @@ class SourceCache extends Evented { if (tile.uses > 0) return; - if (tile.hasData() && tile.state !== 'reloading') { + if ((tile.hasData() && tile.state !== 'reloading') || tile.state === 'empty') { this._cache.add(tile.tileID, tile, tile.getExpiryTimeout()); } else { tile.aborted = true; @@ -953,8 +994,14 @@ class SourceCache extends Evented { _getRenderableCoordinates(symbolLayer?: boolean, includeShadowCasters?: boolean): Array { const coords = this.getRenderableIds(symbolLayer, includeShadowCasters).map((id) => this._tiles[id].tileID); + const isGlobe = this.transform.projection.name === 'globe'; for (const coord of coords) { coord.projMatrix = this.transform.calculateProjMatrix(coord.toUnwrapped()); + if (isGlobe) { + coord.expandedProjMatrix = this.transform.calculateProjMatrix(coord.toUnwrapped(), false, true); + } else { + coord.expandedProjMatrix = coord.projMatrix; + } } return coords; } diff --git a/src/source/source_state.js b/src/source/source_state.js index 4ce549af88c..ba161c75abc 100644 --- a/src/source/source_state.js +++ b/src/source/source_state.js @@ -1,7 +1,7 @@ // @flow import {extend} from '../util/util.js'; -import Tile from './tile.js'; +import type Tile from './tile.js'; import type {FeatureState} from '../style-spec/expression/index.js'; import type Painter from '../render/painter.js'; diff --git a/src/source/tile.js b/src/source/tile.js index 99ffcd0426c..8ed3fe12715 100644 --- a/src/source/tile.js +++ b/src/source/tile.js @@ -27,6 +27,7 @@ import boundsAttributes from '../data/bounds_attributes.js'; import posAttributes, {posAttributesGlobeExt} from '../data/pos_attributes.js'; import EXTENT from '../style-spec/data/extent.js'; import Point from '@mapbox/point-geometry'; +import RasterParticleState from '../render/raster_particle_state.js'; import SegmentVector from '../data/segment.js'; import {transitionTileAABBinECEF, globeNormalizeECEF, tileCoordToECEF, globeToMercatorTransition, interpolateVec3} from '../geo/projection/globe_util.js'; import {vec3, mat4} from 'gl-matrix'; @@ -63,6 +64,7 @@ const CLOCK_SKEW_RETRY_TIMEOUT = 30000; export type TileState = | 'loading' // Tile data is in the process of loading. | 'loaded' // Tile data has been loaded. Tile can be rendered. + | 'empty' // Tile data has been loaded but has no content for rendering. | 'reloading' // Tile data has been loaded and is being updated. Tile can be rendered. | 'unloaded' // Tile data has been deleted. | 'errored' // Tile data was not loaded because of an error. @@ -136,6 +138,7 @@ class Tile { reloadCallback: any; resourceTiming: ?Array; queryPadding: number; + rasterParticleState: ?RasterParticleState; symbolFadeHoldUntil: ?number; hasSymbolBuckets: boolean; @@ -164,7 +167,7 @@ class Tile { * @param size * @private */ - constructor(tileID: OverscaledTileID, size: number, tileZoom: number, painter: any, isRaster?: boolean) { + constructor(tileID: OverscaledTileID, size: number, tileZoom: number, painter: ?Painter, isRaster?: boolean) { this.tileID = tileID; this.uid = uniqueId(); this.uses = 0; @@ -393,18 +396,20 @@ class Tile { } const gl = context.gl; - if (this.imageAtlas && !this.imageAtlas.uploaded) { - this.imageAtlasTexture = new Texture(context, this.imageAtlas.image, gl.RGBA); + const atlas = this.imageAtlas; + if (atlas && !atlas.uploaded) { + const hasPattern = !!Object.keys(atlas.patternPositions).length; + this.imageAtlasTexture = new Texture(context, atlas.image, gl.RGBA, {useMipmap: hasPattern}); ((this.imageAtlas: any): ImageAtlas).uploaded = true; } if (this.glyphAtlasImage) { - this.glyphAtlasTexture = new Texture(context, this.glyphAtlasImage, gl.ALPHA); + this.glyphAtlasTexture = new Texture(context, this.glyphAtlasImage, gl.R8); this.glyphAtlasImage = null; } if (this.lineAtlas && !this.lineAtlas.uploaded) { - this.lineAtlasTexture = new Texture(context, this.lineAtlas.image, gl.ALPHA); + this.lineAtlasTexture = new Texture(context, this.lineAtlas.image, gl.R8); ((this.lineAtlas: any): LineAtlas).uploaded = true; } } @@ -454,7 +459,7 @@ class Tile { } }); - if (!this.latestFeatureIndex || !this.latestFeatureIndex.rawTileData) + if (!this.latestFeatureIndex || !(this.latestFeatureIndex.rawTileData || this.latestFeatureIndex.is3DTile)) return {}; return this.latestFeatureIndex.query({ @@ -968,6 +973,11 @@ class Tile { delete this.demTexture; } + if (this.rasterParticleState) { + this.rasterParticleState.destroy(); + delete this.rasterParticleState; + } + Debug.run(() => { if (this.queryGeometryDebugViz) { this.queryGeometryDebugViz.unload(); diff --git a/src/source/tile_bounds.js b/src/source/tile_bounds.js index f34301502ac..80f7cc93f8b 100644 --- a/src/source/tile_bounds.js +++ b/src/source/tile_bounds.js @@ -1,6 +1,6 @@ // @flow -import LngLatBounds from '../geo/lng_lat_bounds.js'; +import {LngLatBounds} from '../geo/lng_lat.js'; import {mercatorXfromLng, mercatorYfromLat} from '../geo/mercator_coordinate.js'; import type {CanonicalTileID} from './tile_id.js'; diff --git a/src/source/tile_id.js b/src/source/tile_id.js index 207713d05c8..762f766cdf8 100644 --- a/src/source/tile_id.js +++ b/src/source/tile_id.js @@ -61,6 +61,7 @@ export class OverscaledTileID { canonical: CanonicalTileID; key: number; projMatrix: Float32Array; + expandedProjMatrix: Float32Array; constructor(overscaledZ: number, wrap: number, z: number, x: number, y: number) { assert(overscaledZ >= z); @@ -219,4 +220,4 @@ export const neighborCoord = [ ]; register(CanonicalTileID, 'CanonicalTileID'); -register(OverscaledTileID, 'OverscaledTileID', {omit: ['projMatrix']}); +register(OverscaledTileID, 'OverscaledTileID', {omit: ['projMatrix', 'expandedProjMatrix']}); diff --git a/src/source/vector_tile_source.js b/src/source/vector_tile_source.js index 7f534ddb1f3..b5f9115e78e 100644 --- a/src/source/vector_tile_source.js +++ b/src/source/vector_tile_source.js @@ -9,19 +9,19 @@ import TileBounds from './tile_bounds.js'; import {ResourceType} from '../util/ajax.js'; import browser from '../util/browser.js'; import {cacheEntryPossiblyAdded} from '../util/tile_request_cache.js'; -import {DedupedRequest, loadVectorTile} from './vector_tile_worker_source.js'; +import {DedupedRequest, loadVectorTile} from './load_vector_tile.js'; import {makeFQID} from '../util/fqid.js'; import type {Source} from './source.js'; import type {OverscaledTileID} from './tile_id.js'; -import type Map from '../ui/map.js'; +import type {Map} from '../ui/map.js'; import type Dispatcher from '../util/dispatcher.js'; import type Tile from './tile.js'; import type {Callback} from '../types/callback.js'; import type {Cancelable} from '../types/cancelable.js'; import type {VectorSourceSpecification, PromoteIdSpecification} from '../style-spec/types.js'; import type Actor from '../util/actor.js'; -import type {LoadVectorTileResult} from './vector_tile_worker_source.js'; +import type {LoadVectorTileResult} from './load_vector_tile.js'; import type {WorkerTileResult} from './worker_source.js'; /** @@ -122,7 +122,7 @@ class VectorTileSource extends Evented implements Source { if (tileJSON.bounds) this.tileBounds = new TileBounds(tileJSON.bounds, this.minzoom, this.maxzoom); postTurnstileEvent(tileJSON.tiles, this.map._requestManager._customAccessToken); - // `content` is included here to prevent a race condition where `Style#_updateSources` is called + // `content` is included here to prevent a race condition where `Style#updateSources` is called // before the TileJSON arrives. this makes sure the tiles needed are loaded once TileJSON arrives // ref: https://github.com/mapbox/mapbox-gl-js/pull/4347#discussion_r104418088 this.fire(new Event('data', {dataType: 'source', sourceDataType: 'metadata'})); diff --git a/src/source/vector_tile_worker_source.js b/src/source/vector_tile_worker_source.js index 7eed1d87dcb..327d93686ac 100644 --- a/src/source/vector_tile_worker_source.js +++ b/src/source/vector_tile_worker_source.js @@ -1,7 +1,5 @@ // @flow -import {getArrayBuffer} from '../util/ajax.js'; - import {VectorTile} from '@mapbox/vector-tile'; import Protobuf from 'pbf'; import WorkerTile from './worker_tile.js'; @@ -9,127 +7,20 @@ import {extend} from '../util/util.js'; import {getPerformanceMeasurement} from '../util/performance.js'; import {Evented} from '../util/evented.js'; import tileTransform from '../geo/projection/tile_transform.js'; +import {loadVectorTile, DedupedRequest} from './load_vector_tile.js'; import type { WorkerSource, WorkerTileResult, WorkerTileParameters, - RequestedTileParameters, WorkerTileCallback, TileParameters } from './worker_source.js'; import type Actor from '../util/actor.js'; import type StyleLayerIndex from '../style/style_layer_index.js'; -import type {Callback} from '../types/callback.js'; import type Scheduler from '../util/scheduler.js'; -import type {IVectorTile} from '@mapbox/vector-tile'; - -export type LoadVectorTileResult = { - rawData: ArrayBuffer; - vectorTile?: IVectorTile; - expires?: any; - cacheControl?: any; - resourceTiming?: Array; -}; - -/** - * @callback LoadVectorDataCallback - * @param error - * @param vectorTile - * @private - */ -export type LoadVectorDataCallback = Callback; - -export type AbortVectorData = () => void; -export type LoadVectorData = (params: RequestedTileParameters, callback: LoadVectorDataCallback) => ?AbortVectorData; -export class DedupedRequest { - entries: { [string]: Object }; - scheduler: ?Scheduler; - - constructor(scheduler?: Scheduler) { - this.entries = {}; - this.scheduler = scheduler; - } - - request(key: string, metadata: Object, request: any, callback: LoadVectorDataCallback): (() => void) { - const entry = this.entries[key] = this.entries[key] || {callbacks: []}; - - if (entry.result) { - const [err, result] = entry.result; - if (this.scheduler) { - this.scheduler.add(() => { - callback(err, result); - }, metadata); - } else { - callback(err, result); - } - return () => {}; - } - - entry.callbacks.push(callback); - - if (!entry.cancel) { - entry.cancel = request((err, result) => { - entry.result = [err, result]; - for (const cb of entry.callbacks) { - if (this.scheduler) { - this.scheduler.add(() => { - cb(err, result); - }, metadata); - } else { - cb(err, result); - } - } - setTimeout(() => delete this.entries[key], 1000 * 3); - }); - } - - return () => { - if (entry.result) return; - entry.callbacks = entry.callbacks.filter(cb => cb !== callback); - if (!entry.callbacks.length) { - entry.cancel(); - delete this.entries[key]; - } - }; - } -} - -/** - * @private - */ -// $FlowFixMe[missing-this-annot] -export function loadVectorTile(params: RequestedTileParameters, callback: LoadVectorDataCallback, skipParse?: boolean): (() => void) { - const key = JSON.stringify(params.request); - - const makeRequest = (callback: LoadVectorDataCallback) => { - const request = getArrayBuffer(params.request, (err: ?Error, data: ?ArrayBuffer, cacheControl: ?string, expires: ?string) => { - if (err) { - callback(err); - } else if (data) { - callback(null, { - vectorTile: skipParse ? undefined : new VectorTile(new Protobuf(data)), - rawData: data, - cacheControl, - expires - }); - } - }); - return () => { - request.cancel(); - callback(); - }; - }; - - if (params.data) { - // if we already got the result earlier (on the main thread), return it directly - (this.deduped: DedupedRequest).entries[key] = {result: [null, params.data]}; - } - - const callbackMetadata = {type: 'parseTile', isSymbolTile: params.isSymbolTile, zoom: params.tileZoom}; - return (this.deduped: DedupedRequest).request(key, callbackMetadata, makeRequest, callback); -} +import type {LoadVectorData} from './load_vector_tile.js'; /** * The {@link WorkerSource} implementation that supports {@link VectorTileSource}. @@ -277,6 +168,8 @@ class VectorTileWorkerSource extends Evented implements WorkerSource { done(); } } + } else { + callback(null, undefined); } } diff --git a/src/source/video_source.js b/src/source/video_source.js index 264c86eadc7..6663d9975b6 100644 --- a/src/source/video_source.js +++ b/src/source/video_source.js @@ -7,7 +7,7 @@ import Texture from '../render/texture.js'; import {ErrorEvent} from '../util/evented.js'; import ValidationError from '../style-spec/error/validation_error.js'; -import type Map from '../ui/map.js'; +import type {Map} from '../ui/map.js'; import type Dispatcher from '../util/dispatcher.js'; import type {Evented} from '../util/evented.js'; import type {VideoSourceSpecification} from '../style-spec/types.js'; diff --git a/src/source/worker.js b/src/source/worker.js index d5d88679c21..1c53a7c991e 100644 --- a/src/source/worker.js +++ b/src/source/worker.js @@ -5,6 +5,7 @@ import Actor from '../util/actor.js'; import StyleLayerIndex from '../style/style_layer_index.js'; import VectorTileWorkerSource from './vector_tile_worker_source.js'; import RasterDEMTileWorkerSource from './raster_dem_tile_worker_source.js'; +import RasterArrayTileWorkerSource from './raster_array_tile_worker_source.js'; import GeoJSONWorkerSource from './geojson_worker_source.js'; import Tiled3dModelWorkerSource from '../../3d-style/source/tiled_3d_model_worker_source.js'; import assert from 'assert'; @@ -14,19 +15,23 @@ import {PerformanceUtils} from '../util/performance.js'; import {Event} from '../util/evented.js'; import {getProjection} from '../geo/projection/index.js'; +import type {Class} from '../types/class.js'; + import type { WorkerSource, WorkerTileParameters, WorkerDEMTileParameters, WorkerTileCallback, WorkerDEMTileCallback, - TileParameters + TileParameters, + WorkerRasterArrayTileParameters, + WorkerRasterArrayTileCallback } from '../source/worker_source.js'; import type {WorkerGlobalScopeInterface} from '../util/web_worker.js'; import type {Callback} from '../types/callback.js'; import type {LayerSpecification, ProjectionSpecification} from '../style-spec/types.js'; -import type {Expression} from '../style-spec/expression/expression.js'; +import type {ConfigOptions} from '../style/properties.js'; import type {PluginState} from './rtl_text_plugin.js'; import type Projection from '../geo/projection/projection.js'; @@ -41,6 +46,7 @@ export default class Worker { workerSourceTypes: {[_: string]: Class }; workerSources: {[mapId: string]: {[scope: string]: {[sourceType: string]: {[sourceId: string]: WorkerSource}}}}; demWorkerSources: {[mapId: string]: {[scope: string]: {[sourceId: string]: RasterDEMTileWorkerSource }}}; + rasterArrayWorkerSource: ?RasterArrayTileWorkerSource; projections: {[_: string]: Projection }; defaultProjection: Projection; isSpriteLoaded: {[mapId: string]: {[scope: string]: boolean}}; @@ -93,6 +99,7 @@ export default class Worker { delete this.availableImages[mapId]; delete this.workerSources[mapId]; delete this.demWorkerSources[mapId]; + delete this.rasterArrayWorkerSource; callback(); } @@ -157,12 +164,12 @@ export default class Worker { callback(); } - setLayers(mapId: string, params: {layers: Array, scope: string, options: Map}, callback: WorkerTileCallback) { + setLayers(mapId: string, params: {layers: Array, scope: string, options: ConfigOptions}, callback: WorkerTileCallback) { this.getLayerIndex(mapId, params.scope).replace(params.layers, params.options); callback(); } - updateLayers(mapId: string, params: {layers: Array, scope: string, removedIds: Array, options: Map}, callback: WorkerTileCallback) { + updateLayers(mapId: string, params: {layers: Array, scope: string, removedIds: Array, options: ConfigOptions}, callback: WorkerTileCallback) { this.getLayerIndex(mapId, params.scope).update(params.layers, params.removedIds, params.options); callback(); } @@ -177,6 +184,10 @@ export default class Worker { this.getDEMWorkerSource(mapId, params.source, params.scope).loadTile(params, callback); } + decodeRasterArray(mapId: string, params: WorkerRasterArrayTileParameters, callback: WorkerRasterArrayTileCallback) { + this.getRasterArrayWorkerSource().decodeRasterArray(params, callback); + } + reloadTile(mapId: string, params: WorkerTileParameters & {type: string}, callback: WorkerTileCallback) { assert(params.type); params.projection = this.projections[mapId] || this.defaultProjection; @@ -327,6 +338,14 @@ export default class Worker { return this.demWorkerSources[mapId][scope][source]; } + getRasterArrayWorkerSource(): RasterArrayTileWorkerSource { + if (!this.rasterArrayWorkerSource) { + this.rasterArrayWorkerSource = new RasterArrayTileWorkerSource(); + } + + return this.rasterArrayWorkerSource; + } + enforceCacheSizeLimit(mapId: string, limit: number) { enforceCacheSizeLimit(limit); } @@ -336,7 +355,7 @@ export default class Worker { } } -/* global self, WorkerGlobalScope */ +/* global WorkerGlobalScope */ if (typeof WorkerGlobalScope !== 'undefined' && typeof self !== 'undefined' && self instanceof WorkerGlobalScope) { diff --git a/src/source/worker_source.js b/src/source/worker_source.js index efe50a25f1e..be5c772ef48 100644 --- a/src/source/worker_source.js +++ b/src/source/worker_source.js @@ -52,6 +52,11 @@ export type WorkerDEMTileParameters = TileParameters & { convertToFloat: boolean }; +export type WorkerRasterArrayTileParameters = { + buffer: ArrayBuffer, + task: any, +}; + export type WorkerTileResult = { buckets: Array, imageAtlas: ImageAtlas, @@ -70,6 +75,7 @@ export type WorkerTileResult = { export type WorkerTileCallback = (error: ?Error, result: ?WorkerTileResult) => void; export type WorkerDEMTileCallback = (err: ?Error, result: ?DEMData) => void; +export type WorkerRasterArrayTileCallback = (err: ?Error, result: ?any) => void; /** * May be implemented by custom source types to provide code that can be run on diff --git a/src/source/worker_tile.js b/src/source/worker_tile.js index d29b9b61b57..144a5304e0e 100644 --- a/src/source/worker_tile.js +++ b/src/source/worker_tile.js @@ -199,6 +199,7 @@ class WorkerTile { const maybePrepare = () => { if (error) { + this.status = 'done'; return callback(error); } else if (this.extraShadowCaster) { const m = PerformanceUtils.beginMeasure('parseTile2'); diff --git a/src/style-spec/composite.js b/src/style-spec/composite.js index b9858126a15..b7cc0de386e 100644 --- a/src/style-spec/composite.js +++ b/src/style-spec/composite.js @@ -1,3 +1,4 @@ +// @noflow export default function (style) { const styleIDs = []; diff --git a/src/style-spec/diff.js b/src/style-spec/diff.js index bc197eda1b5..39f71072b94 100644 --- a/src/style-spec/diff.js +++ b/src/style-spec/diff.js @@ -148,20 +148,10 @@ export const operations: {[_: string]: string} = { */ removeImport: 'removeImport', - /* - * { command: 'setImportUrl', args: [importId, styleUrl] } - */ - setImportUrl: 'setImportUrl', - - /* - * { command: 'setImportData', args: [importId, stylesheet] } + /** + * { command: 'updateImport', args: [importId, importSpecification | styleUrl] } */ - setImportData: 'setImportData', - - /* - * { command: 'setImportConfig', args: [importId, config] } - */ - setImportConfig: 'setImportConfig' + updateImport: 'updateImport' }; function addSource(sourceId: string, after: Sources, commands: Array) { @@ -424,20 +414,7 @@ export function diffImports(before: Array = [], after: Arra const beforeImport = beforeIndex[afterImport.id]; if (!beforeImport || isEqual(beforeImport, afterImport)) continue; - if (!isEqual(beforeImport.config, afterImport.config)) { - commands.push({command: operations.setImportConfig, args: [afterImport.id, afterImport.config]}); - } - - if (!isEqual(beforeImport.url, afterImport.url)) { - commands.push({command: operations.setImportUrl, args: [afterImport.id, afterImport.url]}); - } - - const beforeData = beforeImport && beforeImport.data; - const afterData = afterImport.data; - - if (!isEqual(beforeData, afterData)) { - commands.push({command: operations.setImportData, args: [afterImport.id, afterData]}); - } + commands.push({command: operations.updateImport, args: [afterImport.id, afterImport]}); } } @@ -462,7 +439,7 @@ export function diffImports(before: Array = [], after: Arra export default function diffStyles(before: StyleSpecification, after: StyleSpecification): Array { if (!before) return [{command: operations.setStyle, args: [after]}]; - let commands = []; + let commands: Array = []; try { // Handle changes to top-level properties @@ -487,6 +464,10 @@ export default function diffStyles(before: StyleSpecification, after: StyleSpeci if (!isEqual(before.glyphs, after.glyphs)) { commands.push({command: operations.setGlyphs, args: [after.glyphs]}); } + // Handle changes to `imports` before other mergable top-level properties + if (!isEqual(before.imports, after.imports)) { + diffImports(before.imports, after.imports, commands); + } if (!isEqual(before.transition, after.transition)) { commands.push({command: operations.setTransition, args: [after.transition]}); } @@ -550,9 +531,6 @@ export default function diffStyles(before: StyleSpecification, after: StyleSpeci // Handle changes to `layers` diffLayers(beforeLayers, after.layers, commands); - - // Handle changes to `imports` - diffImports(before.imports, after.imports, commands); } catch (e) { // fall back to setStyle console.warn('Unable to compute style diff:', e); diff --git a/src/style-spec/expression/compound_expression.js b/src/style-spec/expression/compound_expression.js index 3232e9af5b2..5c7acfcefc7 100644 --- a/src/style-spec/expression/compound_expression.js +++ b/src/style-spec/expression/compound_expression.js @@ -21,17 +21,23 @@ class CompoundExpression implements Expression { type: Type; _evaluate: Evaluate; args: Array; + _overloadIndex: number; static definitions: {[_: string]: Definition }; - constructor(name: string, type: Type, evaluate: Evaluate, args: Array) { + constructor(name: string, type: Type, evaluate: Evaluate, args: Array, overloadIndex: number) { this.name = name; this.type = type; this._evaluate = evaluate; this.args = args; + this._overloadIndex = overloadIndex; } evaluate(ctx: EvaluationContext): Value { + if (!this._evaluate) { // restore evaluate function after transfer between threads + const definition = CompoundExpression.definitions[this.name]; + this._evaluate = Array.isArray(definition) ? definition[2] : definition.overloads[this._overloadIndex][1]; + } return this._evaluate(ctx, this.args); } @@ -62,17 +68,21 @@ class CompoundExpression implements Expression { [[definition[1], definition[2]]] : definition.overloads; - const overloads = availableOverloads.filter(([signature]) => ( - !Array.isArray(signature) || // varags - signature.length === args.length - 1 // correct param count - )); + const overloadParams = []; let signatureContext: ParsingContext = (null: any); - for (const [params, evaluate] of overloads) { + let overloadIndex = -1; + + for (const [params, evaluate] of availableOverloads) { + if (Array.isArray(params) && params.length !== args.length - 1) continue; // param count doesn't match + + overloadParams.push(params); + overloadIndex++; + // Use a fresh context for each attempted signature so that, if // we eventually succeed, we haven't polluted `context.errors`. - signatureContext = new ParsingContext(context.registry, context.path, null, context.scope, undefined, context.options); + signatureContext = new ParsingContext(context.registry, context.path, null, context.scope, undefined, context._scope, context.options); // First parse all the args, potentially coercing to the // types expected by this overload. @@ -111,19 +121,17 @@ class CompoundExpression implements Expression { } if (signatureContext.errors.length === 0) { - return new CompoundExpression(op, type, evaluate, parsedArgs); + return new CompoundExpression(op, type, evaluate, parsedArgs, overloadIndex); } } assert(!signatureContext || signatureContext.errors.length > 0); - if (overloads.length === 1) { + if (overloadParams.length === 1) { context.errors.push(...signatureContext.errors); } else { - const expected = overloads.length ? overloads : availableOverloads; - const signatures = expected - .map(([params]) => stringifySignature(params)) - .join(' | '); + const expected = overloadParams.length ? overloadParams : availableOverloads.map(([params]) => params); + const signatures = expected.map(stringifySignature).join(' | '); const actualTypes = []; // For error message, re-parse arguments without trying to diff --git a/src/style-spec/expression/definitions/config.js b/src/style-spec/expression/definitions/config.js new file mode 100644 index 00000000000..5ba3eaffc93 --- /dev/null +++ b/src/style-spec/expression/definitions/config.js @@ -0,0 +1,142 @@ +// @flow + +import { + type Type, + ValueType +} from '../types.js'; +import {Color, typeOf, toString as valueToString} from '../values.js'; +import Formatted from '../types/formatted.js'; +import ResolvedImage from '../types/resolved_image.js'; +import Literal from './literal.js'; + +import type {Expression, SerializedExpression} from '../expression.js'; +import type ParsingContext from '../parsing_context.js'; +import type EvaluationContext from '../evaluation_context.js'; + +function coerceValue(type: string, value: any): any { + switch (type) { + case 'string': return valueToString(value); + case 'number': return +value; + case 'boolean': return !!value; + case 'color': return Color.parse(value); + case 'formatted': { + return Formatted.fromString(valueToString(value)); + } + case 'resolvedImage': { + return ResolvedImage.fromString(valueToString(value)); + } + } + return value; +} + +function clampToAllowedNumber(value: number, min: number | void, max: number | void, step: number | void): number { + if (step !== undefined) { + value = step * Math.round(value / step); + } + if (min !== undefined && value < min) { + value = min; + } + if (max !== undefined && value > max) { + value = max; + } + return value; +} + +class Config implements Expression { + type: Type; + key: string; + scope: ?string; + + constructor(type: Type, key: string, scope?: string) { + this.type = type; + this.key = key; + this.scope = scope; + } + + static parse(args: $ReadOnlyArray, context: ParsingContext): ?Config { + let type = context.expectedType; + if (type === null || type === undefined) { + type = ValueType; + } + if (args.length < 2 || args.length > 3) { + return context.error(`Invalid number of arguments for 'config' expression.`); + } + + const configKey = context.parse(args[1], 1); + if (!(configKey instanceof Literal)) { + return context.error(`Key name of 'config' expression must be a string literal.`); + } + + if (args.length >= 3) { + const configScope = context.parse(args[2], 2); + if (!(configScope instanceof Literal)) { + return context.error(`Scope of 'config' expression must be a string literal.`); + } + return new Config(type, valueToString(configKey.value), valueToString(configScope.value)); + } + + return new Config(type, valueToString(configKey.value)); + } + + evaluate(ctx: EvaluationContext): any { + const FQIDSeparator = '\u001F'; + const configKey = [this.key, this.scope, ctx.scope].filter(Boolean).join(FQIDSeparator); + + const config = ctx.getConfig(configKey); + if (!config) return null; + + const {type, value, values, minValue, maxValue, stepValue} = config; + + const defaultValue = config.default.evaluate(ctx); + + let result = defaultValue; + if (value) { + // temporarily override scope to parent to evaluate config expressions passed from the parent + const originalScope = ctx.scope; + ctx.scope = (originalScope || '').split(FQIDSeparator).slice(1).join(FQIDSeparator); + result = value.evaluate(ctx); + ctx.scope = originalScope; + } + if (type) { + result = coerceValue(type, result); + } + + if (result !== undefined && (minValue !== undefined || maxValue !== undefined || stepValue !== undefined)) { + if (typeof result === 'number') { + result = clampToAllowedNumber(result, minValue, maxValue, stepValue); + } else if (Array.isArray(result)) { + result = result.map((item) => typeof item === 'number' ? clampToAllowedNumber(item, minValue, maxValue, stepValue) : item); + } + } + + if (value !== undefined && result !== undefined && values && !values.includes(result)) { + // The result is not among the allowed values. Instead, use the default value from the option. + result = defaultValue; + if (type) { + result = coerceValue(type, result); + } + } + + if ((type && type !== this.type) || (result !== undefined && typeOf(result) !== this.type)) { + result = coerceValue(this.type.kind, result); + } + + return result; + } + + eachChild() { } + + outputDefined(): boolean { + return false; + } + + serialize(): SerializedExpression { + const res = ["config", this.key]; + if (this.scope) { + res.concat(this.key); + } + return res; + } +} + +export default Config; diff --git a/src/style-spec/expression/definitions/index.js b/src/style-spec/expression/definitions/index.js index e8b9d137266..4bcf5cf5d1c 100644 --- a/src/style-spec/expression/definitions/index.js +++ b/src/style-spec/expression/definitions/index.js @@ -45,12 +45,13 @@ import FormatExpression from './format.js'; import ImageExpression from './image.js'; import Length from './length.js'; import Within from './within.js'; +import Config from './config.js'; import Distance from './distance.js'; +import {mulberry32} from '../../util/random.js'; import type EvaluationContext from '../evaluation_context.js'; import type {Varargs} from '../compound_expression.js'; import type {Expression, ExpressionRegistry} from '../expression.js'; -import {mulberry32} from '../../util/random.js'; const expressions: ExpressionRegistry = { // special forms @@ -110,7 +111,9 @@ const expressions: ExpressionRegistry = { // $FlowFixMe[method-unbinding] 'within': Within, // $FlowFixMe[method-unbinding] - 'distance': Distance + 'distance': Distance, + // $FlowFixMe[method-unbinding] + 'config': Config }; function rgba(ctx: EvaluationContext, [r, g, b, a]: Array) { @@ -145,14 +148,6 @@ function get(key: string, obj: {[string]: any}) { return typeof v === 'undefined' ? null : v; } -function getConfig(ctx: EvaluationContext, key: string, scope: string) { - if (scope.length) { - key += `\u{1f}${scope}`; - } - const v = ctx.getConfig(key); - return v ? v.evaluate(ctx) : null; -} - function binarySearch(v: any, a: {[number]: any}, i: number, j: number) { while (i <= j) { const m = (i + j) >> 1; @@ -245,18 +240,6 @@ CompoundExpression.register(expressions, { ] ] }, - 'config': { - type: ValueType, - overloads: [ - [ - [StringType], - (ctx, [key]) => getConfig(ctx, key.evaluate(ctx), '') - ], [ - [StringType, StringType], - (ctx, [key, scope]) => getConfig(ctx, key.evaluate(ctx), scope.evaluate(ctx)) - ] - ] - }, 'feature-state': [ ValueType, [StringType], @@ -312,6 +295,11 @@ CompoundExpression.register(expressions, { [], (ctx) => ctx.globals.rasterValue || 0 ], + 'raster-particle-speed': [ + NumberType, + [], + (ctx) => ctx.globals.rasterParticleSpeed || 0 + ], 'sky-radial-progress': [ NumberType, [], diff --git a/src/style-spec/expression/definitions/number_format.js b/src/style-spec/expression/definitions/number_format.js index c187817467d..913f2921075 100644 --- a/src/style-spec/expression/definitions/number_format.js +++ b/src/style-spec/expression/definitions/number_format.js @@ -7,34 +7,6 @@ import type EvaluationContext from '../evaluation_context.js'; import type ParsingContext from '../parsing_context.js'; import type {Type} from '../types.js'; -declare var Intl: { - NumberFormat: Class -}; - -declare class Intl$NumberFormat { - constructor ( - locales?: string | string[], - options?: NumberFormatOptions - ): Intl$NumberFormat; - - static ( - locales?: string | string[], - options?: NumberFormatOptions - ): Intl$NumberFormat; - - format(a: number): string; - - resolvedOptions(): any; -} - -type NumberFormatOptions = { - style?: 'decimal' | 'currency' | 'percent' | 'unit'; - currency?: null | string; - unit?: null | string; - minimumFractionDigits?: null | string; - maximumFractionDigits?: null | string; -}; - export default class NumberFormat implements Expression { type: Type; number: Expression; diff --git a/src/style-spec/expression/evaluation_context.js b/src/style-spec/expression/evaluation_context.js index 7a2ed3bc65e..ff62a2d25c2 100644 --- a/src/style-spec/expression/evaluation_context.js +++ b/src/style-spec/expression/evaluation_context.js @@ -1,13 +1,13 @@ // @flow import {Color} from './values.js'; -import type {Expression} from './expression.js'; import type Point from '@mapbox/point-geometry'; import type {FormattedSection} from './types/formatted.js'; import type {GlobalProperties, Feature, FeatureState} from './index.js'; import type {CanonicalTileID} from '../../source/tile_id.js'; import type {FeatureDistanceData} from '../feature_filter/index.js'; +import type {ConfigOptions, ConfigOptionValue} from '../../style/properties.js'; const geometryTypes = ['Unknown', 'Point', 'LineString', 'Polygon']; @@ -20,11 +20,12 @@ class EvaluationContext { canonical: null | CanonicalTileID; featureTileCoord: ?Point; featureDistanceData: ?FeatureDistanceData; - options: ?Map; + scope: ?string; + options: ?ConfigOptions; _parseColorCache: {[_: string]: ?Color}; - constructor(options?: ?Map) { + constructor(scope: ?string, options: ?ConfigOptions) { this.globals = (null: any); this.feature = null; this.featureState = null; @@ -34,6 +35,7 @@ class EvaluationContext { this.canonical = null; this.featureTileCoord = null; this.featureDistanceData = null; + this.scope = scope; this.options = options; } @@ -92,7 +94,7 @@ class EvaluationContext { return cached; } - getConfig(id: string): ?Expression { + getConfig(id: string): ?ConfigOptionValue { return this.options ? this.options.get(id) : null; } } diff --git a/src/style-spec/expression/expression.js b/src/style-spec/expression/expression.js index fb271f35f0d..b24890d245d 100644 --- a/src/style-spec/expression/expression.js +++ b/src/style-spec/expression/expression.js @@ -1,6 +1,7 @@ // @flow import type {Type} from './types.js'; +import type {Class} from '../../types/class.js'; import type ParsingContext from './parsing_context.js'; import type EvaluationContext from './evaluation_context.js'; diff --git a/src/style-spec/expression/index.js b/src/style-spec/expression/index.js index 6d7b2f83944..d9e6e232d81 100644 --- a/src/style-spec/expression/index.js +++ b/src/style-spec/expression/index.js @@ -33,6 +33,7 @@ import type {FormattedSection} from './types/formatted.js'; import type Point from '@mapbox/point-geometry'; import type {CanonicalTileID} from '../../source/tile_id.js'; import type {FeatureDistanceData} from '../feature_filter/index.js'; +import type {ConfigOptions} from '../../style/properties.js'; export interface Feature { +type: 1 | 2 | 3 | 'Unknown' | 'Point' | 'LineString' | 'Polygon'; @@ -50,6 +51,7 @@ export interface GlobalProperties { heatmapDensity?: number, lineProgress?: number, rasterValue?: number, + rasterParticleSpeed?: number, skyRadialProgress?: number, +isSupportedScript?: (_: string) => boolean, accumulated?: Value, @@ -64,10 +66,10 @@ export class StyleExpression { _warningHistory: {[key: string]: boolean}; _enumValues: ?{[_: string]: any}; - constructor(expression: Expression, propertySpec: ?StylePropertySpecification, options?: ?Map) { + constructor(expression: Expression, propertySpec: ?StylePropertySpecification, scope?: ?string, options?: ?ConfigOptions) { this.expression = expression; this._warningHistory = {}; - this._evaluator = new EvaluationContext(options); + this._evaluator = new EvaluationContext(scope, options); this._defaultValue = propertySpec ? getDefaultValue(propertySpec) : null; this._enumValues = propertySpec && propertySpec.type === 'enum' ? propertySpec.values : null; } @@ -131,8 +133,8 @@ export function isExpression(expression: mixed): boolean { * * @private */ -export function createExpression(expression: mixed, propertySpec: ?StylePropertySpecification, options?: ?Map): Result> { - const parser = new ParsingContext(definitions, [], propertySpec ? getExpectedType(propertySpec) : undefined, undefined, undefined, options); +export function createExpression(expression: mixed, propertySpec: ?StylePropertySpecification, scope?: ?string, options?: ?ConfigOptions): Result> { + const parser = new ParsingContext(definitions, [], propertySpec ? getExpectedType(propertySpec) : undefined, undefined, undefined, scope, options); // For string-valued properties, coerce to string at the top level rather than asserting. const parsed = parser.parse(expression, undefined, undefined, undefined, @@ -143,7 +145,7 @@ export function createExpression(expression: mixed, propertySpec: ?StyleProperty return error(parser.errors); } - return success(new StyleExpression(parsed, propertySpec, options)); + return success(new StyleExpression(parsed, propertySpec, scope, options)); } export class ZoomConstantExpression { @@ -248,8 +250,8 @@ export type StylePropertyExpression = | CameraExpression | CompositeExpression; -export function createPropertyExpression(expression: mixed, propertySpec: StylePropertySpecification, options?: ?Map): Result> { - expression = createExpression(expression, propertySpec, options); +export function createPropertyExpression(expression: mixed, propertySpec: StylePropertySpecification, scope?: ?string, options?: ?ConfigOptions): Result> { + expression = createExpression(expression, propertySpec, scope, options); if (expression.result === 'error') { return expression; } @@ -330,12 +332,12 @@ export class StylePropertyFunction { } } -export function normalizePropertyExpression(value: PropertyValueSpecification, specification: StylePropertySpecification, options?: ?Map): StylePropertyExpression { +export function normalizePropertyExpression(value: PropertyValueSpecification, specification: StylePropertySpecification, scope?: ?string, options?: ?ConfigOptions): StylePropertyExpression { if (isFunction(value)) { return (new StylePropertyFunction(value, specification): any); } else if (isExpression(value) || (Array.isArray(value) && value.length > 0)) { - const expression = createPropertyExpression(value, specification, options); + const expression = createPropertyExpression(value, specification, scope, options); if (expression.result === 'error') { // this should have been caught in validation throw new Error(expression.value.map(err => `${err.key}: ${err.message}`).join(', ')); diff --git a/src/style-spec/expression/is_constant.js b/src/style-spec/expression/is_constant.js index 1ddd13f6cd1..56016cf3a7c 100644 --- a/src/style-spec/expression/is_constant.js +++ b/src/style-spec/expression/is_constant.js @@ -3,6 +3,7 @@ import CompoundExpression from './compound_expression.js'; import Within from './definitions/within.js'; import Distance from './definitions/distance.js'; +import Config from './definitions/config.js'; import type {Expression} from './expression.js'; function isFeatureConstant(e: Expression): boolean { @@ -53,11 +54,10 @@ function isStateConstant(e: Expression): boolean { } function isConfigConstant(e: Expression): boolean { - if (e instanceof CompoundExpression) { - if (e.name === 'config') { - return false; - } + if (e instanceof Config) { + return false; } + let result = true; e.eachChild(arg => { if (result && !isConfigConstant(arg)) { result = false; } diff --git a/src/style-spec/expression/parsing_context.js b/src/style-spec/expression/parsing_context.js index 7a52c7e2664..ae7643d90c9 100644 --- a/src/style-spec/expression/parsing_context.js +++ b/src/style-spec/expression/parsing_context.js @@ -11,11 +11,13 @@ import CompoundExpression from './compound_expression.js'; import CollatorExpression from './definitions/collator.js'; import Within from './definitions/within.js'; import Distance from './definitions/distance.js'; +import Config from './definitions/config.js'; import {isGlobalPropertyConstant, isFeatureConstant} from './is_constant.js'; import Var from './definitions/var.js'; import type {Expression, ExpressionRegistry} from './expression.js'; import type {Type} from './types.js'; +import type {ConfigOptions} from '../../style/properties.js'; /** * State associated parsing at a given point in an expression tree. @@ -27,7 +29,8 @@ class ParsingContext { key: string; scope: Scope; errors: Array; - options: ?Map; + _scope: ?string; + options: ?ConfigOptions; // The expected type of this expression. Provided only to allow Expression // implementations to infer argument types: Expression#parse() need not @@ -41,7 +44,8 @@ class ParsingContext { expectedType: ?Type, scope: Scope = new Scope(), errors: Array = [], - options?: ?Map + _scope: ?string, + options?: ?ConfigOptions ) { this.registry = registry; this.path = path; @@ -49,6 +53,7 @@ class ParsingContext { this.scope = scope; this.errors = errors; this.expectedType = expectedType; + this._scope = _scope; this.options = options; } @@ -123,7 +128,7 @@ class ParsingContext { // parsed/compiled result. Expressions that expect an image should // not be resolved here so we can later get the available images. if (!(parsed instanceof Literal) && (parsed.type.kind !== 'resolvedImage') && isConstant(parsed)) { - const ec = new EvaluationContext(this.options); + const ec = new EvaluationContext(this._scope, this.options); try { parsed = new Literal(parsed.type, parsed.evaluate(ec)); } catch (e) { @@ -163,6 +168,7 @@ class ParsingContext { expectedType || null, scope, this.errors, + this._scope, this.options ); } @@ -197,17 +203,17 @@ function isConstant(expression: Expression) { return isConstant(expression.boundExpression); } else if (expression instanceof CompoundExpression && expression.name === 'error') { return false; - } else if (expression instanceof CompoundExpression && expression.name === 'config') { - return false; } else if (expression instanceof CollatorExpression) { // Although the results of a Collator expression with fixed arguments // generally shouldn't change between executions, we can't serialize them // as constant expressions because results change based on environment. return false; - } else if (expression instanceof Within) { + } else if (expression instanceof Within) { return false; } else if (expression instanceof Distance) { return false; + } else if (expression instanceof Config) { + return false; } const isTypeAnnotation = expression instanceof Coercion || @@ -233,5 +239,5 @@ function isConstant(expression: Expression) { } return isFeatureConstant(expression) && - isGlobalPropertyConstant(expression, ['zoom', 'heatmap-density', 'line-progress', 'raster-value', 'sky-radial-progress', 'accumulated', 'is-supported-script', 'pitch', 'distance-from-center', 'measure-light']); + isGlobalPropertyConstant(expression, ['zoom', 'heatmap-density', 'line-progress', 'raster-value', 'sky-radial-progress', 'accumulated', 'is-supported-script', 'pitch', 'distance-from-center', 'measure-light', 'raster-particle-speed']); } diff --git a/src/style-spec/expression/types/collator.js b/src/style-spec/expression/types/collator.js index c9bcd61589f..26e1efa1274 100644 --- a/src/style-spec/expression/types/collator.js +++ b/src/style-spec/expression/types/collator.js @@ -1,37 +1,5 @@ // @flow -// Flow type declarations for Intl cribbed from -// https://github.com/facebook/flow/issues/1270 - -declare var Intl: { - Collator: Class -}; - -declare class Intl$Collator { - constructor ( - locales?: string | string[], - options?: CollatorOptions - ): Intl$Collator; - - static ( - locales?: string | string[], - options?: CollatorOptions - ): Intl$Collator; - - compare (a: string, b: string): number; - - resolvedOptions(): any; -} - -type CollatorOptions = { - localeMatcher?: 'lookup' | 'best fit', - usage?: 'sort' | 'search', - sensitivity?: 'base' | 'accent' | 'case' | 'variant', - ignorePunctuation?: boolean, - numeric?: boolean, - caseFirst?: 'upper' | 'lower' | 'false' -} - export default class Collator { locale: string | null; sensitivity: 'base' | 'accent' | 'case' | 'variant'; diff --git a/src/style-spec/format.js b/src/style-spec/format.js index d32d0e4abf3..95e40ce92ef 100644 --- a/src/style-spec/format.js +++ b/src/style-spec/format.js @@ -1,3 +1,4 @@ +// @noflow import reference from './reference/latest.js'; import stringifyPretty from 'json-stringify-pretty-compact'; diff --git a/src/style-spec/function/index.js b/src/style-spec/function/index.js index 6400f6d2f5a..62e19802189 100644 --- a/src/style-spec/function/index.js +++ b/src/style-spec/function/index.js @@ -1,3 +1,4 @@ +// @noflow import * as colorSpaces from '../util/color_spaces.js'; import Color from '../util/color.js'; diff --git a/src/style-spec/migrate.js b/src/style-spec/migrate.js index b8cc0cfa3c8..ae06d116bcc 100644 --- a/src/style-spec/migrate.js +++ b/src/style-spec/migrate.js @@ -1,3 +1,4 @@ +// @noflow import migrateToV8 from './migrate/v8.js'; import migrateToExpressions from './migrate/expressions.js'; diff --git a/src/style-spec/migrate/v8.js b/src/style-spec/migrate/v8.js index 8d0be733535..9c3e3eb0015 100644 --- a/src/style-spec/migrate/v8.js +++ b/src/style-spec/migrate/v8.js @@ -1,5 +1,4 @@ - -import URL from 'url'; +// @noflow import {eachSource, eachLayer, eachProperty} from '../visit.js'; function eachLayout(layer, callback) { @@ -109,7 +108,7 @@ export default function(style) { }); function migrateFontstackURL(input) { - const inputParsed = URL.parse(input); + const inputParsed = new URL(input); const inputPathnameParts = inputParsed.pathname.split('/'); if (inputParsed.protocol !== 'mapbox:') { diff --git a/src/style-spec/migrate/v9.js b/src/style-spec/migrate/v9.js index 3c084abe01d..e9fc1db98e7 100644 --- a/src/style-spec/migrate/v9.js +++ b/src/style-spec/migrate/v9.js @@ -1,4 +1,4 @@ - +// @noflow import deref from '../deref.js'; function eachLayer(style, callback) { diff --git a/src/style-spec/package.json b/src/style-spec/package.json index bc29820534d..43b1247cb47 100644 --- a/src/style-spec/package.json +++ b/src/style-spec/package.json @@ -1,7 +1,7 @@ { "name": "@mapbox/mapbox-gl-style-spec", "description": "a specification for mapbox gl styles", - "version": "14.0.0", + "version": "14.4.0-beta.1", "author": "Mapbox", "keywords": [ "mapbox", diff --git a/src/style-spec/read_style.js b/src/style-spec/read_style.js index 6a75779712c..6d8a6c64665 100644 --- a/src/style-spec/read_style.js +++ b/src/style-spec/read_style.js @@ -1,3 +1,5 @@ +// @noflow + import ParsingError from './error/parsing_error.js'; import jsonlint from '@mapbox/jsonlint-lines-primitives'; diff --git a/src/style-spec/reference/v8.json b/src/style-spec/reference/v8.json index f05b570b232..d6c813be415 100644 --- a/src/style-spec/reference/v8.json +++ b/src/style-spec/reference/v8.json @@ -92,13 +92,13 @@ "basic functionality": { "js": "3.0.0", "android": "11.0.0", - "ios": "11.0.0", - "macos": "11.0.0" + "ios": "11.0.0" } } }, "terrain": { "type": "terrain", + "optional": true, "doc": "A global modifier that elevates layers and markers based on a DEM data source." }, "fog": { @@ -117,8 +117,7 @@ "basic functionality": { "js": "3.0.0", "android": "11.0.0", - "ios": "11.0.0", - "macos": "11.0.0" + "ios": "11.0.0" } } }, @@ -129,8 +128,7 @@ "basic functionality": { "js": "3.0.0", "android": "11.0.0", - "ios": "11.0.0", - "macos": "11.0.0" + "ios": "11.0.0" } } }, @@ -611,6 +609,7 @@ "source_vector", "source_raster", "source_raster_dem", + "source_raster_array", "source_geojson", "source_video", "source_image", @@ -629,12 +628,12 @@ }, "url": { "type": "string", - "doc": "A URL to a TileJSON resource. Supported protocols are `http:`, `https:`, and `mapbox://`." + "doc": "A URL to a TileJSON resource. Supported protocols are `http:`, `https:`, and `mapbox://`. Required if `tiles` is not provided." }, "tiles": { "type": "array", "value": "string", - "doc": "An array of one or more tile source URLs, as in the TileJSON spec." + "doc": "An array of one or more tile source URLs, as in the TileJSON spec. Required if `url` is not provided." }, "bounds": { "type": "array", @@ -708,12 +707,12 @@ }, "url": { "type": "string", - "doc": "A URL to a TileJSON resource. Supported protocols are `http:`, `https:`, and `mapbox://`." + "doc": "A URL to a TileJSON resource. Supported protocols are `http:`, `https:`, and `mapbox://`. Required if `tiles` is not provided." }, "tiles": { "type": "array", "value": "string", - "doc": "An array of one or more tile source URLs, as in the TileJSON spec." + "doc": "An array of one or more tile source URLs, as in the TileJSON spec. Required if `url` is not provided." }, "bounds": { "type": "array", @@ -789,12 +788,12 @@ }, "url": { "type": "string", - "doc": "A URL to a TileJSON resource. Supported protocols are `http:`, `https:`, and `mapbox://`." + "doc": "A URL to a TileJSON resource. Supported protocols are `http:`, `https:`, and `mapbox://`. Required if `tiles` is not provided." }, "tiles": { "type": "array", "value": "string", - "doc": "An array of one or more tile source URLs, as in the TileJSON spec." + "doc": "An array of one or more tile source URLs, as in the TileJSON spec. Required if `url` is not provided." }, "bounds": { "type": "array", @@ -857,6 +856,78 @@ "doc": "Other keys to configure the data source." } }, + "source_raster_array": { + "type": { + "required": true, + "type": "enum", + "values": { + "raster-array": { + "doc": "A raster array source" + } + }, + "doc": "The type of the source." + }, + "url": { + "type": "string", + "doc": "A URL to a TileJSON resource. Supported protocols are `http:`, `https:`, and `mapbox://`. Required if `tiles` is not provided." + }, + "tiles": { + "type": "array", + "value": "string", + "doc": "An array of one or more tile source URLs, as in the TileJSON spec. Required if `url` is not provided." + }, + "bounds": { + "type": "array", + "value": "number", + "length": 4, + "default": [ + -180, + -85.051129, + 180, + 85.051129 + ], + "doc": "An array containing the longitude and latitude of the southwest and northeast corners of the source's bounding box in the following order: `[sw.lng, sw.lat, ne.lng, ne.lat]`. When this property is included in a source, no tiles outside of the given bounds are requested by Mapbox GL." + }, + "minzoom": { + "type": "number", + "default": 0, + "doc": "Minimum zoom level for which tiles are available, as in the TileJSON spec." + }, + "maxzoom": { + "type": "number", + "default": 22, + "doc": "Maximum zoom level for which tiles are available, as in the TileJSON spec. Data from tiles at the maxzoom are used when displaying the map at higher zoom levels." + }, + "tileSize": { + "type": "number", + "default": 512, + "units": "pixels", + "doc": "The minimum visual size to display tiles for this layer. Only configurable for raster layers." + }, + "attribution": { + "type": "string", + "doc": "Contains an attribution to be displayed when the map is shown to a user." + }, + "rasterLayers": { + "type": "*", + "doc": "Contains the description of the raster data layers and the bands contained within the tiles." + }, + "volatile": { + "type": "boolean", + "default": false, + "doc": "A setting to determine whether a source's tiles are cached locally.", + "sdk-support": { + "basic functionality": { + "android": "9.3.0", + "ios": "5.10.0" + } + } + }, + "*": { + "type": "*", + "doc": "Other keys to configure the data source." + } + }, "source_geojson": { "type": { "required": true, @@ -877,6 +948,11 @@ "default": 18, "doc": "Maximum zoom level at which to create vector tiles (higher means greater detail at high zoom levels)." }, + "minzoom": { + "type": "number", + "default": 0, + "doc": "Minimum zoom level at which to create vector tiles" + }, "attribution": { "type": "string", "doc": "Contains an attribution to be displayed when the map is shown to a user." @@ -1039,8 +1115,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" } } }, @@ -1050,8 +1125,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" } } }, @@ -1061,8 +1135,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" } } }, @@ -1072,8 +1145,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" } } }, @@ -1083,8 +1155,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -1094,8 +1165,7 @@ "basic functionality": { "js": "0.27.0", "android": "5.1.0", - "ios": "3.6.0", - "macos": "0.5.0" + "ios": "3.6.0" } } }, @@ -1105,8 +1175,17 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" + } + } + }, + "raster-particle": { + "doc": "Particle animation driven by textures such as wind maps.", + "sdk-support": { + "basic functionality": { + "js": "3.3.0", + "android": "11.4.0", + "ios": "11.4.0" } } }, @@ -1116,8 +1195,7 @@ "basic functionality": { "js": "0.43.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -1137,8 +1215,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" } } }, @@ -1158,8 +1235,7 @@ "basic functionality": { "js": "3.0.0", "android": "11.0.0", - "ios": "11.0.0", - "macos": "11.0.0" + "ios": "11.0.0" } } } @@ -1177,7 +1253,7 @@ }, "source-layer": { "type": "string", - "doc": "Layer to use from a vector tile source. Required for vector tile sources; prohibited for all other source types, including GeoJSON sources." + "doc": "Layer to use from a vector tile source. Required for vector and raster-array sources; prohibited for all other source types, including GeoJSON sources." }, "slot": { "type": "string", @@ -1186,8 +1262,7 @@ "basic functionality": { "js": "3.0.0", "android": "11.0.0", - "ios": "11.0.0", - "macos": "11.0.0" + "ios": "11.0.0" } } }, @@ -1224,6 +1299,7 @@ "layout_fill-extrusion", "layout_symbol", "layout_raster", + "layout_raster-particle", "layout_hillshade", "layout_background", "layout_sky", @@ -1246,14 +1322,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "expressions support": { "js": "3.0.0", "android": "11.0.0", - "ios": "11.0.0", - "macos": "11.0.0" + "ios": "11.0.0" } }, "expression": { @@ -1284,8 +1358,7 @@ "expressions support": { "js": "3.0.0", "android": "11.0.0", - "ios": "11.0.0", - "macos": "11.0.0" + "ios": "11.0.0" } }, "expression": { @@ -1311,8 +1384,7 @@ "basic functionality": { "js": "3.0.0", "android": "11.0.0", - "ios": "11.0.0", - "macos": "11.0.0" + "ios": "11.0.0" } }, "expression": { @@ -1323,7 +1395,7 @@ "model-id": { "type": "string", "default": "", - "doc": "Model to render.", + "doc": "Model to render. It can be either a string referencing an element to the models root property or an internal or external URL", "property-type": "data-driven", "expression": { "interpolated": false, @@ -1362,14 +1434,12 @@ "basic functionality": { "js": "1.2.0", "android": "9.1.0", - "ios": "5.8.0", - "macos": "0.15.0" + "ios": "5.8.0" }, "data-driven styling": { "js": "1.2.0", "android": "9.1.0", - "ios": "5.8.0", - "macos": "0.15.0" + "ios": "5.8.0" } }, "expression": { @@ -1397,14 +1467,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "expressions support": { "js": "3.0.0", "android": "11.0.0", - "ios": "11.0.0", - "macos": "11.0.0" + "ios": "11.0.0" } }, "expression": { @@ -1421,14 +1489,12 @@ "basic functionality": { "js": "1.2.0", "android": "9.2.0", - "ios": "5.9.0", - "macos": "0.16.0" + "ios": "5.9.0" }, "data-driven styling": { "js": "1.2.0", "android": "9.2.0", - "ios": "5.9.0", - "macos": "0.16.0" + "ios": "5.9.0" } }, "expression": { @@ -1456,14 +1522,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "expressions support": { "js": "3.0.0", "android": "11.0.0", - "ios": "11.0.0", - "macos": "11.0.0" + "ios": "11.0.0" } }, "expression": { @@ -1489,14 +1553,12 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" }, "expressions support": { "js": "3.0.0", "android": "11.0.0", - "ios": "11.0.0", - "macos": "11.0.0" + "ios": "11.0.0" } }, "expression": { @@ -1522,14 +1584,12 @@ "basic functionality": { "js": "0.27.0", "android": "5.1.0", - "ios": "3.6.0", - "macos": "0.5.0" + "ios": "3.6.0" }, "expressions support": { "js": "3.0.0", "android": "11.0.0", - "ios": "11.0.0", - "macos": "11.0.0" + "ios": "11.0.0" } }, "expression": { @@ -1553,8 +1613,7 @@ "expressions support": { "js": "3.0.0", "android": "11.0.0", - "ios": "11.0.0", - "macos": "11.0.0" + "ios": "11.0.0" } }, "expression": { @@ -1583,8 +1642,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "2.3.0" @@ -1610,6 +1668,9 @@ }, "miter": { "doc": "A join with a sharp, angled corner which is drawn with the outer sides beyond the endpoint of the path until they meet." + }, + "none": { + "doc": "Line segments are not joined together, each one creates a separate line. Useful in combination with line-pattern. Line-cap property is not respected. Can't be used with data-driven styling." } }, "default": "miter", @@ -1618,14 +1679,17 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "0.40.0", "android": "5.2.0", - "ios": "3.7.0", - "macos": "0.6.0" + "ios": "3.7.0" + }, + "`none` value": { + "js": "3.4.0", + "android": "11.5.0", + "ios": "11.5.0" } }, "expression": { @@ -1650,8 +1714,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" } }, "expression": { @@ -1675,8 +1738,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" } }, "expression": { @@ -1694,14 +1756,12 @@ "basic functionality": { "js": "1.2.0", "android": "9.1.0", - "ios": "5.8.0", - "macos": "0.15.0" + "ios": "5.8.0" }, "data-driven styling": { "js": "1.2.0", "android": "9.1.0", - "ios": "5.8.0", - "macos": "0.15.0" + "ios": "5.8.0" } }, "expression": { @@ -1729,14 +1789,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "expressions support": { "js": "3.0.0", "android": "11.0.0", - "ios": "11.0.0", - "macos": "11.0.0" + "ios": "11.0.0" } }, "expression": { @@ -1765,14 +1823,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "`line-center` value": { "js": "0.47.0", "android": "6.4.0", - "ios": "4.3.0", - "macos": "0.10.0" + "ios": "4.3.0" } }, "expression": { @@ -1798,8 +1854,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" } }, "expression": { @@ -1818,8 +1873,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" } }, "expression": { @@ -1837,14 +1891,12 @@ "basic functionality": { "js": "0.53.0", "android": "7.4.0", - "ios": "4.11.0", - "macos": "0.14.0" + "ios": "4.11.0" }, "data-driven styling": { "js": "0.53.0", "android": "7.4.0", - "ios": "4.11.0", - "macos": "0.14.0" + "ios": "4.11.0" } }, "expression": { @@ -1875,8 +1927,7 @@ "basic functionality": { "js": "0.49.0", "android": "6.6.0", - "ios": "4.5.0", - "macos": "0.12.0" + "ios": "4.5.0" } }, "expression": { @@ -1929,8 +1980,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" } }, "expression": { @@ -1952,8 +2002,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" } }, "expression": { @@ -1976,8 +2025,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" } }, "expression": { @@ -2010,14 +2058,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "`auto` value": { "js": "0.25.0", "android": "4.2.0", - "ios": "3.4.0", - "macos": "0.3.0" + "ios": "3.4.0" } }, "expression": { @@ -2041,14 +2087,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "0.35.0", "android": "5.1.0", - "ios": "3.6.0", - "macos": "0.5.0" + "ios": "3.6.0" } }, "expression": { @@ -2086,14 +2130,12 @@ "basic functionality": { "js": "0.21.0", "android": "4.2.0", - "ios": "3.4.0", - "macos": "0.2.1" + "ios": "3.4.0" }, "stretchable icons": { "js": "1.6.0", "android": "9.2.0", - "ios": "5.8.0", - "macos": "0.15.0" + "ios": "5.8.0" }, "data-driven styling": { "js": "3.0.0", @@ -2137,8 +2179,7 @@ "basic functionality": { "js": "0.21.0", "android": "4.2.0", - "ios": "3.4.0", - "macos": "0.2.1" + "ios": "3.4.0" }, "data-driven styling": { "js": "3.0.0", @@ -2163,14 +2204,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "0.35.0", "android": "5.1.0", - "ios": "3.6.0", - "macos": "0.5.0" + "ios": "3.6.0" } }, "expression": { @@ -2195,14 +2234,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "0.21.0", "android": "5.0.0", - "ios": "3.5.0", - "macos": "0.4.0" + "ios": "3.5.0" } }, "expression": { @@ -2227,8 +2264,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" } }, "expression": { @@ -2259,8 +2295,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" } }, "expression": { @@ -2287,14 +2322,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "0.29.0", "android": "5.0.0", - "ios": "3.5.0", - "macos": "0.4.0" + "ios": "3.5.0" } }, "expression": { @@ -2346,14 +2379,12 @@ "basic functionality": { "js": "0.40.0", "android": "5.2.0", - "ios": "3.7.0", - "macos": "0.6.0" + "ios": "3.7.0" }, "data-driven styling": { "js": "0.40.0", "android": "5.2.0", - "ios": "3.7.0", - "macos": "0.6.0" + "ios": "3.7.0" } }, "expression": { @@ -2387,8 +2418,7 @@ "basic functionality": { "js": "0.39.0", "android": "5.2.0", - "ios": "3.7.0", - "macos": "0.6.0" + "ios": "3.7.0" } }, "expression": { @@ -2421,14 +2451,12 @@ "basic functionality": { "js": "0.21.0", "android": "4.2.0", - "ios": "3.4.0", - "macos": "0.2.1" + "ios": "3.4.0" }, "`auto` value": { "js": "0.25.0", "android": "4.2.0", - "ios": "3.4.0", - "macos": "0.3.0" + "ios": "3.4.0" } }, "expression": { @@ -2461,14 +2489,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "`auto` value": { "js": "0.25.0", "android": "4.2.0", - "ios": "3.4.0", - "macos": "0.3.0" + "ios": "3.4.0" } }, "expression": { @@ -2488,14 +2514,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "0.33.0", "android": "5.0.0", - "ios": "3.5.0", - "macos": "0.4.0" + "ios": "3.5.0" } }, "expression": { @@ -2522,14 +2546,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "0.43.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } }, "expression": { @@ -2554,14 +2576,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "0.35.0", "android": "5.1.0", - "ios": "3.6.0", - "macos": "0.5.0" + "ios": "3.6.0" } }, "expression": { @@ -2591,14 +2611,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "0.40.0", "android": "5.2.0", - "ios": "3.7.0", - "macos": "0.6.0" + "ios": "3.7.0" } }, "expression": { @@ -2622,8 +2640,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "2.3.0", @@ -2652,14 +2669,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "0.40.0", "android": "5.2.0", - "ios": "3.7.0", - "macos": "0.6.0" + "ios": "3.7.0" } }, "expression": { @@ -2696,20 +2711,17 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "0.39.0", "android": "5.2.0", - "ios": "3.7.0", - "macos": "0.6.0" + "ios": "3.7.0" }, "auto": { "js": "0.54.0", "android": "7.4.0", - "ios": "4.10.0", - "macos": "0.14.0" + "ios": "4.10.0" } }, "expression": { @@ -2730,14 +2742,12 @@ "basic functionality": { "js": "0.54.0", "android": "7.4.0", - "ios": "4.10.0", - "macos": "0.14.0" + "ios": "4.10.0" }, "data-driven styling": { "js": "0.54.0", "android": "7.4.0", - "ios": "4.10.0", - "macos": "0.14.0" + "ios": "4.10.0" } }, "requires": [ @@ -2797,8 +2807,7 @@ "basic functionality": { "js": "0.54.0", "android": "7.4.0", - "ios": "4.10.0", - "macos": "0.14.0" + "ios": "4.10.0" } }, "expression": { @@ -2852,14 +2861,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "0.39.0", "android": "5.2.0", - "ios": "3.7.0", - "macos": "0.6.0" + "ios": "3.7.0" } }, "expression": { @@ -2889,8 +2896,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" } }, "expression": { @@ -2920,8 +2926,7 @@ "basic functionality": { "js": "1.3.0", "android": "8.3.0", - "ios": "5.3.0", - "macos": "0.15.0" + "ios": "5.3.0" } }, "expression": { @@ -2945,14 +2950,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "0.35.0", "android": "5.1.0", - "ios": "3.6.0", - "macos": "0.5.0" + "ios": "3.6.0" } }, "expression": { @@ -2977,8 +2980,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" } }, "expression": { @@ -3009,8 +3011,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" } }, "expression": { @@ -3043,14 +3044,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "0.33.0", "android": "5.0.0", - "ios": "3.5.0", - "macos": "0.4.0" + "ios": "3.5.0" } }, "expression": { @@ -3082,14 +3081,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "0.35.0", "android": "5.1.0", - "ios": "3.6.0", - "macos": "0.5.0" + "ios": "3.6.0" } }, "expression": { @@ -3112,8 +3109,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" } }, "expression": { @@ -3135,8 +3131,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" } }, "expression": { @@ -3159,8 +3154,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" } }, "expression": { @@ -3187,14 +3181,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "expressions support": { "js": "3.0.0", "android": "11.0.0", - "ios": "11.0.0", - "macos": "11.0.0" + "ios": "11.0.0" } }, "expression": { @@ -3220,14 +3212,43 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "expressions support": { "js": "3.0.0", "android": "11.0.0", - "ios": "11.0.0", - "macos": "11.0.0" + "ios": "11.0.0" + } + }, + "expression": { + "interpolated": false + }, + "property-type": "constant" + } + }, + "layout_raster-particle": { + "visibility": { + "type": "enum", + "values": { + "visible": { + "doc": "The layer is shown." + }, + "none": { + "doc": "The layer is not shown." + } + }, + "default": "visible", + "doc": "Whether this layer is displayed.", + "sdk-support": { + "basic functionality": { + "js": "3.3.0", + "android": "11.4.0", + "ios": "11.4.0" + }, + "expressions support": { + "js": "3.3.0", + "android": "11.4.0", + "ios": "11.4.0" } }, "expression": { @@ -3253,14 +3274,12 @@ "basic functionality": { "js": "0.43.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" }, "expressions support": { "js": "3.0.0", "android": "11.0.0", - "ios": "11.0.0", - "macos": "11.0.0" + "ios": "11.0.0" } }, "expression": { @@ -3490,8 +3509,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -3502,8 +3520,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -3514,8 +3531,7 @@ "basic functionality": { "js": "3.0.0", "android": "11.0.0", - "ios": "11.0.0", - "macos": "11.0.0" + "ios": "11.0.0" } } }, @@ -3526,8 +3542,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -3538,8 +3553,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -3550,8 +3564,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -3562,8 +3575,7 @@ "basic functionality": { "js": "1.6.0", "android": "9.1.0", - "ios": "5.8.0", - "macos": "0.15.0" + "ios": "5.8.0" } } }, @@ -3596,8 +3608,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -3608,8 +3619,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -3620,8 +3630,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -3632,8 +3641,7 @@ "basic functionality": { "js": "0.42.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -3644,8 +3652,7 @@ "basic functionality": { "js": "0.42.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -3674,8 +3681,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -3686,8 +3692,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -3698,8 +3703,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -3710,8 +3714,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -3722,8 +3725,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -3734,8 +3736,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -3746,8 +3747,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -3758,8 +3758,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -3770,8 +3769,7 @@ "basic functionality": { "js": "0.45.0", "android": "6.5.0", - "ios": "4.2.0", - "macos": "0.9.0" + "ios": "4.2.0" } } }, @@ -3782,44 +3780,38 @@ "basic functionality": { "js": "0.48.0", "android": "6.7.0", - "ios": "4.6.0", - "macos": "0.12.0" + "ios": "4.6.0" }, "text-font": { "js": "0.48.0", "android": "6.7.0", - "ios": "4.6.0", - "macos": "0.12.0" + "ios": "4.6.0" }, "font-scale": { "js": "0.48.0", "android": "6.7.0", - "ios": "4.6.0", - "macos": "0.12.0" + "ios": "4.6.0" }, "text-color": { "js": "1.3.0", "android": "7.3.0", - "ios": "4.10.0", - "macos": "0.14.0" + "ios": "4.10.0" }, "image": { "js": "1.6.0", "android": "8.6.0", - "ios": "5.7.0", - "macos": "0.15.0" + "ios": "5.7.0" } } }, "image": { - "doc": "Returns a [`ResolvedImage`](/mapbox-gl-js/style-spec/types/#resolvedimage) for use in [`icon-image`](/mapbox-gl-js/style-spec/layers/#layout-symbol-icon-image), `*-pattern` entries, and as a section in the [`'format'`](#types-format) expression. A [`'coalesce'`](#coalesce) expression containing `image` expressions will evaluate to the first listed image that is currently in the style. This validation process is synchronous and requires the image to have been added to the style before requesting it in the `'image'` argument.", + "doc": "Returns a [`ResolvedImage`](/mapbox-gl-js/style-spec/types/#resolvedimage) for use in [`icon-image`](/mapbox-gl-js/style-spec/layers/#layout-symbol-icon-image), `*-pattern` entries, and as a section in the [`'format'`](#types-format) expression. A [`'coalesce'`](#coalesce) expression containing `image` expressions will evaluate to the first listed image that is currently in the style. This validation process is synchronous and requires the image to have been added to the style before requesting it in the `'image'` argument. To implement crossfading between two images within a symbol layer using the [`icon-image-cross-fade`](/mapbox-gl-js/style-spec/layers/#paint-symbol-icon-image-cross-fade) attribute, include a second image as the second argument in the `'image'` expression.", "group": "Types", "sdk-support": { "basic functionality": { "js": "1.4.0", "android": "8.6.0", - "ios": "5.7.0", - "macos": "0.15.0" + "ios": "5.7.0" } } }, @@ -3830,8 +3822,7 @@ "basic functionality": { "js": "0.54.0", "android" : "8.4.0", - "ios": "5.4.0", - "macos": "0.15.0" + "ios": "5.4.0" } } }, @@ -3842,8 +3833,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -3854,8 +3844,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -3866,8 +3855,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -3878,8 +3866,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -3890,8 +3877,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -3902,8 +3888,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -3914,8 +3899,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -3948,8 +3932,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -3960,8 +3943,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -3972,8 +3954,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -3984,8 +3965,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -4007,8 +3987,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -4019,8 +3998,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -4031,8 +4009,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -4076,8 +4053,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -4088,8 +4064,7 @@ "basic functionality": { "js": "0.45.0", "android": "6.5.0", - "ios": "4.6.0", - "macos": "0.12.0" + "ios": "4.6.0" } } }, @@ -4111,8 +4086,7 @@ "basic functionality": { "js": "0.53.0", "android": "8.4.0", - "ios": "5.5.0", - "macos": "0.15.0" + "ios": "5.5.0" } } }, @@ -4123,8 +4097,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -4135,8 +4108,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -4147,8 +4119,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -4159,8 +4130,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -4171,8 +4141,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -4183,8 +4152,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -4195,8 +4163,7 @@ "basic functionality": { "js": "0.42.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -4207,8 +4174,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -4219,8 +4185,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -4231,8 +4196,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -4243,8 +4207,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -4255,8 +4218,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -4267,8 +4229,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -4279,8 +4240,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -4291,8 +4251,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -4303,8 +4262,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -4315,8 +4273,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -4327,8 +4284,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -4339,8 +4295,7 @@ "basic functionality": { "js": "0.45.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -4351,8 +4306,7 @@ "basic functionality": { "js": "0.45.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -4363,8 +4317,7 @@ "basic functionality": { "js": "0.45.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -4375,8 +4328,7 @@ "basic functionality": { "js": "0.45.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -4387,8 +4339,7 @@ "basic functionality": { "js": "3.0.0", "android": "9.2.0", - "ios": "5.9.0", - "macos": "0.16.0" + "ios": "5.9.0" } } }, @@ -4399,14 +4350,12 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" }, "collator": { "js": "0.45.0", "android": "6.5.0", - "ios": "4.2.0", - "macos": "0.9.0" + "ios": "4.2.0" } } }, @@ -4417,14 +4366,12 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" }, "collator": { "js": "0.45.0", "android": "6.5.0", - "ios": "4.2.0", - "macos": "0.9.0" + "ios": "4.2.0" } } }, @@ -4435,14 +4382,12 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" }, "collator": { "js": "0.45.0", "android": "6.5.0", - "ios": "4.2.0", - "macos": "0.9.0" + "ios": "4.2.0" } } }, @@ -4453,14 +4398,12 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" }, "collator": { "js": "0.45.0", "android": "6.5.0", - "ios": "4.2.0", - "macos": "0.9.0" + "ios": "4.2.0" } } }, @@ -4471,14 +4414,12 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" }, "collator": { "js": "0.45.0", "android": "6.5.0", - "ios": "4.2.0", - "macos": "0.9.0" + "ios": "4.2.0" } } }, @@ -4489,14 +4430,12 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" }, "collator": { "js": "0.45.0", "android": "6.5.0", - "ios": "4.2.0", - "macos": "0.9.0" + "ios": "4.2.0" } } }, @@ -4507,8 +4446,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -4519,8 +4457,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -4531,8 +4468,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -4543,8 +4479,7 @@ "basic functionality": { "js": "1.9.0", "android": "9.1.0", - "ios": "5.8.0", - "macos": "0.15.0" + "ios": "5.8.0" } } }, @@ -4555,8 +4490,7 @@ "basic functionality": { "js": "0.45.0", "android": "6.6.0", - "ios": "4.1.0", - "macos": "0.8.0" + "ios": "4.1.0" } } }, @@ -4567,8 +4501,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -4579,8 +4512,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -4591,8 +4523,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } } }, @@ -4603,8 +4534,7 @@ "basic functionality": { "js": "0.45.0", "android": "6.5.0", - "ios": "4.2.0", - "macos": "0.9.0" + "ios": "4.2.0" } } }, @@ -4619,6 +4549,17 @@ } } }, + "raster-particle-speed": { + "doc": "Returns the length of the particle velocity vector. Can only be used in the `raster-particle-color` property.", + "group": "Raster Particle Animation", + "sdk-support": { + "basic functionality": { + "js": "3.3.0", + "android": "11.4.0", + "ios": "11.4.0" + } + } + }, "random": { "doc": "Returns a random value in the specified range (first two input numbers) based on a supplied seed (third input). The seed can be an expression or a constant number or string value.", "group": "Math", @@ -4902,8 +4843,7 @@ "basic functionality": { "js": "0.27.0", "android": "5.1.0", - "ios": "3.6.0", - "macos": "0.5.0" + "ios": "3.6.0" } } }, @@ -4934,8 +4874,7 @@ "basic functionality": { "js": "0.27.0", "android": "5.1.0", - "ios": "3.6.0", - "macos": "0.5.0" + "ios": "3.6.0" } } }, @@ -4955,8 +4894,7 @@ "basic functionality": { "js": "0.27.0", "android": "5.1.0", - "ios": "3.6.0", - "macos": "0.5.0" + "ios": "3.6.0" } } }, @@ -4978,8 +4916,7 @@ "basic functionality": { "js": "0.27.0", "android": "5.1.0", - "ios": "3.6.0", - "macos": "0.5.0" + "ios": "3.6.0" } } } @@ -5140,8 +5077,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" } }, "expression": { @@ -5163,14 +5099,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "0.21.0", "android": "5.0.0", - "ios": "3.5.0", - "macos": "0.4.0" + "ios": "3.5.0" } }, "expression": { @@ -5198,14 +5132,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "0.19.0", "android": "5.0.0", - "ios": "3.5.0", - "macos": "0.4.0" + "ios": "3.5.0" } }, "expression": { @@ -5235,14 +5167,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "0.19.0", "android": "5.0.0", - "ios": "3.5.0", - "macos": "0.4.0" + "ios": "3.5.0" } }, "expression": { @@ -5271,8 +5201,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" } }, "expression": { @@ -5302,8 +5231,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" } }, "expression": { @@ -5322,13 +5250,11 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "0.49.0", "android": "6.5.0", - "macos": "0.11.0", "ios": "4.4.0" } }, @@ -5380,8 +5306,7 @@ "basic functionality": { "js": "0.27.0", "android": "5.1.0", - "ios": "3.6.0", - "macos": "0.5.0" + "ios": "3.6.0" } }, "expression": { @@ -5406,14 +5331,12 @@ "basic functionality": { "js": "0.27.0", "android": "5.1.0", - "ios": "3.6.0", - "macos": "0.5.0" + "ios": "3.6.0" }, "data-driven styling": { "js": "0.27.0", "android": "5.1.0", - "ios": "3.6.0", - "macos": "0.5.0" + "ios": "3.6.0" } }, "expression": { @@ -5442,8 +5365,7 @@ "basic functionality": { "js": "0.27.0", "android": "5.1.0", - "ios": "3.6.0", - "macos": "0.5.0" + "ios": "3.6.0" } }, "expression": { @@ -5473,8 +5395,7 @@ "basic functionality": { "js": "0.27.0", "android": "5.1.0", - "ios": "3.6.0", - "macos": "0.5.0" + "ios": "3.6.0" } }, "expression": { @@ -5493,13 +5414,11 @@ "basic functionality": { "js": "0.27.0", "android": "5.1.0", - "ios": "3.6.0", - "macos": "0.5.0" + "ios": "3.6.0" }, "data-driven styling": { "js": "0.49.0", "android": "6.5.0", - "macos": "0.11.0", "ios": "4.4.0" } }, @@ -5523,14 +5442,12 @@ "basic functionality": { "js": "0.27.0", "android": "5.1.0", - "ios": "3.6.0", - "macos": "0.5.0" + "ios": "3.6.0" }, "data-driven styling": { "js": "0.27.0", "android": "5.1.0", - "ios": "3.6.0", - "macos": "0.5.0" + "ios": "3.6.0" } }, "expression": { @@ -5557,14 +5474,12 @@ "basic functionality": { "js": "0.27.0", "android": "5.1.0", - "ios": "3.6.0", - "macos": "0.5.0" + "ios": "3.6.0" }, "data-driven styling": { "js": "0.27.0", "android": "5.1.0", - "ios": "3.6.0", - "macos": "0.5.0" + "ios": "3.6.0" } }, "expression": { @@ -5586,8 +5501,7 @@ "basic functionality": { "js": "0.50.0", "android": "7.0.0", - "ios": "4.7.0", - "macos": "0.13.0" + "ios": "4.7.0" } }, "expression": { @@ -5613,12 +5527,6 @@ }, "transition": true, "doc": "Controls the intensity of shading near ground and concave angles between walls. Default value 0.0 disables ambient occlusion and values around 0.3 provide the most plausible results for buildings.", - "requires": [ - "lights", - { - "!": "fill-extrusion-flood-light-intensity" - } - ], "sdk-support": { "basic functionality": { "js": "3.0.0", @@ -5642,10 +5550,7 @@ "transition": true, "doc": "Shades area near ground and concave angles between walls where the radius defines only vertical impact. Default value 3.0 corresponds to height of one floor and brings the most plausible results for buildings. This property works only with legacy light. When 3D lights are enabled `fill-extrusion-ambient-occlusion-wall-radius` and `fill-extrusion-ambient-occlusion-ground-radius` are used instead.", "requires": [ - "fill-extrusion-edge-radius", - { - "!": "fill-extrusion-flood-light-intensity" - } + "fill-extrusion-edge-radius" ], "sdk-support": { "basic functionality": { @@ -5670,10 +5575,7 @@ "doc": "Shades area near ground and concave angles between walls where the radius defines only vertical impact. Default value 3.0 corresponds to height of one floor and brings the most plausible results for buildings.", "requires": [ "lights", - "fill-extrusion-edge-radius", - { - "!": "fill-extrusion-flood-light-intensity" - } + "fill-extrusion-edge-radius" ], "sdk-support": { "basic functionality": { @@ -5697,10 +5599,7 @@ "transition": true, "doc": "The extent of the ambient occlusion effect on the ground beneath the extruded buildings in meters.", "requires": [ - "lights", - { - "!": "fill-extrusion-flood-light-intensity" - } + "lights" ], "sdk-support": { "basic functionality": { @@ -5718,10 +5617,7 @@ "maximum": 1.0, "doc": "Provides a control to futher fine-tune the look of the ambient occlusion on the ground beneath the extruded buildings. Lower values give the effect a more solid look while higher values make it smoother.", "requires": [ - "lights", - { - "!": "fill-extrusion-flood-light-intensity" - } + "lights" ], "transition": true, "expression": { @@ -5744,10 +5640,7 @@ "default": "#ffffff", "doc": "The color of the flood light effect on the walls of the extruded buildings.", "requires": [ - "lights", - { - "!": "fill-extrusion-ambient-occlusion-intensity" - } + "lights" ], "transition": true, "expression": { @@ -5773,10 +5666,7 @@ "maximum": 1.0, "doc": "The intensity of the flood light color.", "requires": [ - "lights", - { - "!": "fill-extrusion-ambient-occlusion-intensity" - } + "lights" ], "transition": true, "expression": { @@ -5802,10 +5692,7 @@ "minimum": 0, "doc": "The extent of the flood light effect on the walls of the extruded buildings in meters.", "requires": [ - "lights", - { - "!": "fill-extrusion-ambient-occlusion-intensity" - } + "lights" ], "transition": true, "expression": { @@ -5833,13 +5720,9 @@ "type": "number", "units": "meters", "default": 0, - "minimum": 0, "doc": "The extent of the flood light effect on the ground beneath the extruded buildings in meters.", "requires": [ - "lights", - { - "!": "fill-extrusion-ambient-occlusion-intensity" - } + "lights" ], "transition": true, "expression": { @@ -5870,10 +5753,7 @@ "maximum": 1.0, "doc": "Provides a control to futher fine-tune the look of the flood light on the ground beneath the extruded buildings. Lower values give the effect a more solid look while higher values make it smoother.", "requires": [ - "lights", - { - "!": "fill-extrusion-ambient-occlusion-intensity" - } + "lights" ], "transition": true, "expression": { @@ -5939,7 +5819,7 @@ "default": 0.0, "minimum": 0.0, "maximum": 1.0, - "doc": "This parameter defines the range for the fade-out effect before an automatic content cutoff on pitched map views. The automatic cutoff range is calculated according to the minimum required zoom level of the source and layer. The fade range is expressed in relation to the height of the map view. A value of 1.0 indicates that the content is faded to the same extent as the map's height in pixels, while a value close to zero represents a sharp cutoff. When the value is set to 0.0, the cutoff is completely disabled. Note: The property has no effect on the map if terrain is enabled.", + "doc": "This parameter defines the range for the fade-out effect before an automatic content cutoff on pitched map views. Fade out is implemented by scaling down and removing buildings in the fade range in a staggered fashion. Opacity is not changed. The fade range is expressed in relation to the height of the map view. A value of 1.0 indicates that the content is faded to the same extent as the map's height in pixels, while a value close to zero represents a sharp cutoff. When the value is set to 0.0, the cutoff is completely disabled. Note: The property has no effect on the map if terrain is enabled.", "transition": false, "expression": { "interpolated": false @@ -5952,7 +5832,33 @@ } }, "property-type": "data-constant" - } + }, + "fill-extrusion-emissive-strength": { + "type": "number", + "default": 0, + "minimum": 0, + "transition": true, + "units": "intensity", + "doc": "Controls the intensity of light emitted on the source features.", + "requires": [ + "lights" + ], + "sdk-support": { + "basic functionality": { + "js": "3.0.0", + "android": "11.0.0", + "ios": "11.0.0" + } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "measure-light" + ] + }, + "property-type": "data-constant" + } }, "paint_line": { "line-opacity": { @@ -5966,14 +5872,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "0.29.0", "android": "5.0.0", - "ios": "3.5.0", - "macos": "0.4.0" + "ios": "3.5.0" } }, "expression": { @@ -6001,14 +5905,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "0.23.0", "android": "5.0.0", - "ios": "3.5.0", - "macos": "0.4.0" + "ios": "3.5.0" } }, "expression": { @@ -6037,8 +5939,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" } }, "expression": { @@ -6068,8 +5969,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" } }, "expression": { @@ -6091,14 +5991,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "0.39.0", "android": "5.2.0", - "ios": "3.7.0", - "macos": "0.6.0" + "ios": "3.7.0" } }, "expression": { @@ -6123,14 +6021,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "0.29.0", "android": "5.0.0", - "ios": "3.5.0", - "macos": "0.4.0" + "ios": "3.5.0" } }, "expression": { @@ -6154,14 +6050,12 @@ "basic functionality": { "js": "0.12.1", "android": "3.0.0", - "ios": "3.1.0", - "macos": "0.1.0" + "ios": "3.1.0" }, "data-driven styling": { "js": "0.29.0", "android": "5.0.0", - "ios": "3.5.0", - "macos": "0.4.0" + "ios": "3.5.0" } }, "expression": { @@ -6186,14 +6080,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "0.29.0", "android": "5.0.0", - "ios": "3.5.0", - "macos": "0.4.0" + "ios": "3.5.0" } }, "expression": { @@ -6223,8 +6115,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "2.3.0" @@ -6247,13 +6138,11 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "0.49.0", "android": "6.5.0", - "macos": "0.11.0", "ios": "4.4.0" } }, @@ -6302,8 +6191,7 @@ "basic functionality": { "js": "0.45.0", "android": "6.5.0", - "ios": "4.4.0", - "macos": "0.11.0" + "ios": "4.4.0" }, "data-driven styling": {} }, @@ -6336,8 +6224,7 @@ "basic functionality": { "js": "2.9.0", "android": "10.5.0", - "ios": "10.5.0", - "macos": "10.5.0" + "ios": "10.5.0" } }, "property-type": "constant" @@ -6438,14 +6325,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "0.18.0", "android": "5.0.0", - "ios": "3.5.0", - "macos": "0.4.0" + "ios": "3.5.0" } }, "expression": { @@ -6468,14 +6353,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "0.18.0", "android": "5.0.0", - "ios": "3.5.0", - "macos": "0.4.0" + "ios": "3.5.0" } }, "expression": { @@ -6498,14 +6381,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "0.20.0", "android": "5.0.0", - "ios": "3.5.0", - "macos": "0.4.0" + "ios": "3.5.0" } }, "expression": { @@ -6530,14 +6411,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "0.20.0", "android": "5.0.0", - "ios": "3.5.0", - "macos": "0.4.0" + "ios": "3.5.0" } }, "expression": { @@ -6566,8 +6445,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" } }, "expression": { @@ -6597,8 +6475,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" } }, "expression": { @@ -6625,8 +6502,7 @@ "basic functionality": { "js": "0.21.0", "android": "4.2.0", - "ios": "3.4.0", - "macos": "0.2.1" + "ios": "3.4.0" } }, "expression": { @@ -6653,8 +6529,7 @@ "basic functionality": { "js": "0.39.0", "android": "5.2.0", - "ios": "3.7.0", - "macos": "0.6.0" + "ios": "3.7.0" } }, "expression": { @@ -6676,14 +6551,12 @@ "basic functionality": { "js": "0.29.0", "android": "5.0.0", - "ios": "3.5.0", - "macos": "0.4.0" + "ios": "3.5.0" }, "data-driven styling": { "js": "0.29.0", "android": "5.0.0", - "ios": "3.5.0", - "macos": "0.4.0" + "ios": "3.5.0" } }, "expression": { @@ -6706,14 +6579,12 @@ "basic functionality": { "js": "0.29.0", "android": "5.0.0", - "ios": "3.5.0", - "macos": "0.4.0" + "ios": "3.5.0" }, "data-driven styling": { "js": "0.29.0", "android": "5.0.0", - "ios": "3.5.0", - "macos": "0.4.0" + "ios": "3.5.0" } }, "expression": { @@ -6738,14 +6609,12 @@ "basic functionality": { "js": "0.29.0", "android": "5.0.0", - "ios": "3.5.0", - "macos": "0.4.0" + "ios": "3.5.0" }, "data-driven styling": { "js": "0.29.0", "android": "5.0.0", - "ios": "3.5.0", - "macos": "0.4.0" + "ios": "3.5.0" } }, "expression": { @@ -6798,14 +6667,12 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" }, "data-driven styling": { "js": "0.43.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } }, "expression": { @@ -6829,14 +6696,12 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" }, "data-driven styling": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } }, "expression": { @@ -6860,8 +6725,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } }, "expression": { @@ -6901,8 +6765,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" }, "data-driven styling": {} }, @@ -6925,8 +6788,7 @@ "basic functionality": { "js": "0.41.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } }, "expression": { @@ -6953,14 +6815,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "0.33.0", "android": "5.0.0", - "ios": "3.5.0", - "macos": "0.4.0" + "ios": "3.5.0" } }, "expression": { @@ -7048,14 +6908,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "0.33.0", "android": "5.0.0", - "ios": "3.5.0", - "macos": "0.4.0" + "ios": "3.5.0" } }, "expression": { @@ -7081,14 +6939,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "0.33.0", "android": "5.0.0", - "ios": "3.5.0", - "macos": "0.4.0" + "ios": "3.5.0" } }, "expression": { @@ -7116,14 +6972,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "0.33.0", "android": "5.0.0", - "ios": "3.5.0", - "macos": "0.4.0" + "ios": "3.5.0" } }, "expression": { @@ -7151,14 +7005,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "0.33.0", "android": "5.0.0", - "ios": "3.5.0", - "macos": "0.4.0" + "ios": "3.5.0" } }, "expression": { @@ -7190,8 +7042,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" } }, "expression": { @@ -7222,8 +7073,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" } }, "expression": { @@ -7249,6 +7099,9 @@ "measure-light" ] }, + "requires": [ + "icon-image" + ], "sdk-support": { "basic functionality": { "js": "3.0.0", @@ -7278,14 +7131,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "0.33.0", "android": "5.0.0", - "ios": "3.5.0", - "macos": "0.4.0" + "ios": "3.5.0" } }, "expression": { @@ -7312,14 +7163,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "0.33.0", "android": "5.0.0", - "ios": "3.5.0", - "macos": "0.4.0" + "ios": "3.5.0" } }, "expression": { @@ -7345,14 +7194,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "0.33.0", "android": "5.0.0", - "ios": "3.5.0", - "macos": "0.4.0" + "ios": "3.5.0" } }, "expression": { @@ -7380,14 +7227,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "0.33.0", "android": "5.0.0", - "ios": "3.5.0", - "macos": "0.4.0" + "ios": "3.5.0" } }, "expression": { @@ -7415,14 +7260,12 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" }, "data-driven styling": { "js": "0.33.0", "android": "5.0.0", - "ios": "3.5.0", - "macos": "0.4.0" + "ios": "3.5.0" } }, "expression": { @@ -7454,8 +7297,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" } }, "expression": { @@ -7486,8 +7328,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" } }, "expression": { @@ -7497,6 +7338,82 @@ ] }, "property-type": "data-constant" + }, + "icon-color-saturation": { + "type": "number", + "default": 0, + "minimum": -1, + "maximum": 1, + "transition": false, + "doc": "Increase or reduce the saturation of the symbol icon.", + "sdk-support": { + "basic functionality": { + "js": "3.1.0", + "android": "11.1.0", + "ios": "11.1.0" + } + }, + "expression": { + "interpolated": false + }, + "property-type": "data-constant" + }, + "icon-color-contrast": { + "type": "number", + "default": 0, + "minimum": -1, + "maximum": 1, + "transition": false, + "doc": "Increase or reduce the contrast of the symbol icon.", + "sdk-support": { + "basic functionality": { + "js": "3.5.0", + "android": "11.5.0", + "ios": "11.5.0" + } + }, + "expression": { + "interpolated": false + }, + "property-type": "data-constant" + }, + "icon-color-brightness-min": { + "type": "number", + "doc": "Increase or reduce the brightness of the symbols. The value is the minimum brightness.", + "default": 0, + "minimum": 0, + "maximum": 1, + "transition": false, + "sdk-support": { + "basic functionality": { + "js": "3.5.0", + "android": "11.5.0", + "ios": "11.5.0" + } + }, + "expression": { + "interpolated": false + }, + "property-type": "data-constant" + }, + "icon-color-brightness-max": { + "type": "number", + "doc": "Increase or reduce the brightness of the symbols. The value is the maximum brightness.", + "default": 1, + "minimum": 0, + "maximum": 1, + "transition": false, + "sdk-support": { + "basic functionality": { + "js": "3.5.0", + "android": "11.5.0", + "ios": "11.5.0" + } + }, + "expression": { + "interpolated": false + }, + "property-type": "data-constant" } }, "paint_raster": { @@ -7511,8 +7428,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" } }, "expression": { @@ -7581,10 +7497,6 @@ }, "raster-color-range": { "type": "array", - "default": [ - 0, - 1 - ], "length": 2, "value": "number", "property-type": "data-constant", @@ -7598,7 +7510,7 @@ "zoom" ] }, - "doc": "When `raster-color` is active, specifies the range over which `raster-color` is tabulated. Units correspond to the computed raster value via `raster-color-mix`.", + "doc": "When `raster-color` is active, specifies the range over which `raster-color` is tabulated. Units correspond to the computed raster value via `raster-color-mix`. For `rasterarray` sources, if `raster-color-range` is unspecified, the source's stated data range is used.", "example": [ 0.5, 10 @@ -7622,8 +7534,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" } }, "expression": { @@ -7645,8 +7556,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" } }, "expression": { @@ -7668,8 +7578,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" } }, "expression": { @@ -7691,8 +7600,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" } }, "expression": { @@ -7714,8 +7622,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" } }, "expression": { @@ -7742,8 +7649,7 @@ "basic functionality": { "js": "0.47.0", "android": "6.3.0", - "ios": "4.2.0", - "macos": "0.9.0" + "ios": "4.2.0" } }, "expression": { @@ -7765,8 +7671,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" } }, "expression": { @@ -7776,6 +7681,200 @@ ] }, "property-type": "data-constant" + }, + "raster-emissive-strength": { + "type": "number", + "default": 0, + "minimum": 0, + "transition": true, + "units": "intensity", + "doc": "Controls the intensity of light emitted on the source features.", + "requires": [ + "lights" + ], + "sdk-support": { + "basic functionality": { + "js": "3.0.0", + "android": "11.0.0", + "ios": "11.0.0" + } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "measure-light" + ] + }, + "property-type": "data-constant" + }, + "raster-array-band": { + "type": "string", + "required": false, + "property-type": "data-constant", + "transition": false, + "requires": [ + { + "source": "raster-array" + } + ], + "doc": "Displayed band of raster array source layer. Defaults to the first band if not set.", + "example": "band-name", + "sdk-support": { + "basic functionality": { + "js": "3.1.0", + "android": "11.1.0", + "ios": "11.1.0" + } + } + }, + "raster-elevation": { + "type": "number", + "doc": "Specifies an uniform elevation from the ground, in meters.", + "default": 0, + "minimum": 0, + "transition": true, + "sdk-support": { + "basic functionality": { + "js": "3.1.0", + "android": "11.2.0", + "ios": "11.2.0" + } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" + } + }, + "paint_raster-particle": { + "raster-particle-array-band": { + "type": "string", + "required": false, + "property-type": "data-constant", + "transition": false, + "doc": "Displayed band of raster array source layer", + "example": "\"1713348000\"", + "sdk-support": { + "basic functionality": { + "js": "3.3.0", + "android": "11.4.0", + "ios": "11.4.0" + } + } + }, + "raster-particle-count": { + "type": "number", + "doc": "Defines the amount of particles per tile.", + "default": 512, + "minimum": 1, + "transition": false, + "sdk-support": { + "basic functionality": { + "js": "3.3.0", + "android": "11.4.0", + "ios": "11.4.0" + } + }, + "property-type": "data-constant" + }, + "raster-particle-color": { + "type": "color", + "doc": "Defines a color map by which to colorize a raster particle layer, parameterized by the `[\"raster-particle-speed\"]` expression and evaluated at 256 uniformly spaced steps over the range specified by `raster-particle-max-speed`.", + "transition": false, + "sdk-support": { + "basic functionality": { + "js": "3.3.0", + "android": "11.4.0", + "ios": "11.4.0" + }, + "data-driven styling": {} + }, + "expression": { + "interpolated": true, + "parameters": [ + "raster-particle-speed" + ] + }, + "property-type": "color-ramp" + }, + "raster-particle-max-speed": { + "type": "number", + "doc": "Defines the maximum speed for particles. Velocities with magnitudes equal to or exceeding this value are clamped to the max value.", + "default": 1, + "minimum": 1, + "transition": false, + "sdk-support": { + "basic functionality": { + "js": "3.3.0", + "android": "11.4.0", + "ios": "11.4.0" + } + }, + "property-type": "data-constant" + }, + "raster-particle-speed-factor": { + "type": "number", + "doc": "Defines a coefficient for the speed of particles’ motion.", + "default": 0.2, + "minimum": 0, + "maximum": 1, + "transition": true, + "sdk-support": { + "basic functionality": { + "js": "3.3.0", + "android": "11.4.0", + "ios": "11.4.0" + } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" + }, + "raster-particle-fade-opacity-factor": { + "type": "number", + "doc": "Defines defines the opacity coefficient applied to the faded particles in each frame. In practice, this property controls the length of the particle tail.", + "default": 0.98, + "minimum": 0, + "maximum": 1, + "transition": true, + "sdk-support": { + "basic functionality": { + "js": "3.3.0", + "android": "11.4.0", + "ios": "11.4.0" + } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" + }, + "raster-particle-reset-rate-factor": { + "type": "number", + "doc": "Defines a coefficient for a time period at which particles will restart at a random position, to avoid degeneration (empty areas without particles).", + "default": 0.8, + "minimum": 0, + "maximum": 1, + "transition": false, + "sdk-support": { + "basic functionality": { + "js": "3.3.0", + "android": "11.4.0", + "ios": "11.4.0" + } + }, + "property-type": "data-constant" } }, "paint_hillshade": { @@ -7790,8 +7889,7 @@ "basic functionality": { "js": "0.43.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } }, "expression": { @@ -7818,8 +7916,7 @@ "basic functionality": { "js": "0.43.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } }, "expression": { @@ -7841,8 +7938,7 @@ "basic functionality": { "js": "0.43.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } }, "expression": { @@ -7862,8 +7958,7 @@ "basic functionality": { "js": "0.43.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } }, "expression": { @@ -7884,8 +7979,7 @@ "basic functionality": { "js": "0.43.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } }, "expression": { @@ -7906,8 +8000,7 @@ "basic functionality": { "js": "0.43.0", "android": "6.0.0", - "ios": "4.0.0", - "macos": "0.7.0" + "ios": "4.0.0" } }, "expression": { @@ -7961,8 +8054,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" } }, "expression": { @@ -7981,8 +8073,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" } }, "expression": { @@ -8004,8 +8095,7 @@ "basic functionality": { "js": "0.10.0", "android": "2.0.1", - "ios": "2.0.0", - "macos": "0.1.0" + "ios": "2.0.0" } }, "expression": { @@ -8508,7 +8598,7 @@ "minimum": 0, "maximum": 5, "units": "intensity", - "doc": "Strength of the emission. There is no emission for value 0. For value 1.0, only emissive component (no shading) is displayed and values above 1.0 produce light contribution to surrounding area, for some of the parts (e.g. doors). Expressions that depend on measure-light are not supported when using GeoJSON or vector tile as the model layer source.", + "doc": "Strength of the emission. There is no emission for value 0. For value 1.0, only emissive component (no shading) is displayed and values above 1.0 produce light contribution to surrounding area, for some of the parts (e.g. doors). Expressions that depend on measure-light are only supported as a global layer value (and not for each feature) when using GeoJSON or vector tile as the model layer source.", "expression": { "interpolated": true, "parameters": ["feature", "feature-state", "measure-light"] diff --git a/src/style-spec/rollup.config.js b/src/style-spec/rollup.config.js index a93738776af..9c31e0d43ca 100644 --- a/src/style-spec/rollup.config.js +++ b/src/style-spec/rollup.config.js @@ -1,3 +1,5 @@ +// @noflow + import path from 'path'; import replace from '@rollup/plugin-replace'; import resolve from '@rollup/plugin-node-resolve'; diff --git a/src/style-spec/style-spec.js b/src/style-spec/style-spec.js index 105ae632150..511738c2216 100644 --- a/src/style-spec/style-spec.js +++ b/src/style-spec/style-spec.js @@ -1,7 +1,7 @@ // @flow type ExpressionType = 'data-driven' | 'color-ramp' | 'data-constant' | 'constant'; -type ExpressionParameters = Array<'zoom' | 'feature' | 'feature-state' | 'heatmap-density' | 'line-progress' | 'raster-value' | 'sky-radial-progress' | 'pitch' | 'distance-from-center'>; +type ExpressionParameters = Array<'zoom' | 'feature' | 'feature-state' | 'heatmap-density' | 'line-progress' | 'raster-value' | 'sky-radial-progress' | 'pitch' | 'distance-from-center' | 'measure-light' | 'raster-particle-speed'>; export type ExpressionSpecification = { interpolated: boolean, diff --git a/src/style-spec/test.js b/src/style-spec/test.js index 0d679175efc..86ef03fd33d 100755 --- a/src/style-spec/test.js +++ b/src/style-spec/test.js @@ -1,4 +1,5 @@ #!/usr/bin/env node +// @noflow /* eslint-disable no-process-exit */ import fs from 'fs'; diff --git a/src/style-spec/types.js b/src/style-spec/types.js index 3229d67c7ee..702a57a5de9 100644 --- a/src/style-spec/types.js +++ b/src/style-spec/types.js @@ -68,7 +68,7 @@ export type StyleSpecification = {| "pitch"?: number, "light"?: LightSpecification, "lights"?: Array, - "terrain"?: TerrainSpecification, + "terrain"?: ?TerrainSpecification, "fog"?: FogSpecification, "camera"?: CameraSpecification, "imports"?: Array, @@ -190,10 +190,25 @@ export type RasterDEMSourceSpecification = { [_: string]: mixed } +export type RasterArraySourceSpecification = { + "type": "raster-array", + "url"?: string, + "tiles"?: Array, + "bounds"?: [number, number, number, number], + "minzoom"?: number, + "maxzoom"?: number, + "tileSize"?: number, + "attribution"?: string, + "rasterLayers"?: mixed, + "volatile"?: boolean, + [_: string]: mixed +} + export type GeoJSONSourceSpecification = {| "type": "geojson", "data"?: mixed, "maxzoom"?: number, + "minzoom"?: number, "attribution"?: string, "buffer"?: number, "filter"?: mixed, @@ -231,6 +246,7 @@ export type SourceSpecification = | VectorSourceSpecification | RasterSourceSpecification | RasterDEMSourceSpecification + | RasterArraySourceSpecification | GeoJSONSourceSpecification | VideoSourceSpecification | ImageSourceSpecification @@ -410,7 +426,11 @@ export type SymbolLayerSpecification = {| "text-halo-width"?: DataDrivenPropertyValueSpecification, "text-halo-blur"?: DataDrivenPropertyValueSpecification, "text-translate"?: PropertyValueSpecification<[number, number]>, - "text-translate-anchor"?: PropertyValueSpecification<"map" | "viewport"> + "text-translate-anchor"?: PropertyValueSpecification<"map" | "viewport">, + "icon-color-saturation"?: ExpressionSpecification, + "icon-color-contrast"?: ExpressionSpecification, + "icon-color-brightness-min"?: ExpressionSpecification, + "icon-color-brightness-max"?: ExpressionSpecification |} |} @@ -501,7 +521,8 @@ export type FillExtrusionLayerSpecification = {| "fill-extrusion-flood-light-ground-attenuation"?: PropertyValueSpecification, "fill-extrusion-vertical-scale"?: PropertyValueSpecification, "fill-extrusion-rounded-roof"?: PropertyValueSpecification, - "fill-extrusion-cutoff-fade-range"?: ExpressionSpecification + "fill-extrusion-cutoff-fade-range"?: ExpressionSpecification, + "fill-extrusion-emissive-strength"?: PropertyValueSpecification |} |} @@ -529,7 +550,34 @@ export type RasterLayerSpecification = {| "raster-saturation"?: PropertyValueSpecification, "raster-contrast"?: PropertyValueSpecification, "raster-resampling"?: PropertyValueSpecification<"linear" | "nearest">, - "raster-fade-duration"?: PropertyValueSpecification + "raster-fade-duration"?: PropertyValueSpecification, + "raster-emissive-strength"?: PropertyValueSpecification, + "raster-array-band"?: string, + "raster-elevation"?: PropertyValueSpecification + |} +|} + +export type RasterParticleLayerSpecification = {| + "id": string, + "type": "raster-particle", + "metadata"?: mixed, + "source": string, + "source-layer"?: string, + "slot"?: string, + "minzoom"?: number, + "maxzoom"?: number, + "filter"?: FilterSpecification, + "layout"?: {| + "visibility"?: ExpressionSpecification + |}, + "paint"?: {| + "raster-particle-array-band"?: string, + "raster-particle-count"?: number, + "raster-particle-color"?: ExpressionSpecification, + "raster-particle-max-speed"?: number, + "raster-particle-speed-factor"?: PropertyValueSpecification, + "raster-particle-fade-opacity-factor"?: PropertyValueSpecification, + "raster-particle-reset-rate-factor"?: number |} |} @@ -645,6 +693,7 @@ export type LayerSpecification = | HeatmapLayerSpecification | FillExtrusionLayerSpecification | RasterLayerSpecification + | RasterParticleLayerSpecification | HillshadeLayerSpecification | ModelLayerSpecification | BackgroundLayerSpecification diff --git a/src/style-spec/util/properties.js b/src/style-spec/util/properties.js index d8d9664b504..d14529fe2ec 100644 --- a/src/style-spec/util/properties.js +++ b/src/style-spec/util/properties.js @@ -2,7 +2,9 @@ import type {ExpressionSpecification, StylePropertySpecification} from '../style-spec.js'; -function expressionHasParameter(expression: ?ExpressionSpecification, parameter: string): boolean { +type ExpressionParameter = ExpressionSpecification['parameters'][number]; + +function expressionHasParameter(expression: ?ExpressionSpecification, parameter: ExpressionParameter): boolean { return !!expression && !!expression.parameters && expression.parameters.indexOf(parameter) > -1; } diff --git a/src/style-spec/validate/validate_layer.js b/src/style-spec/validate/validate_layer.js index 2153f52b2bf..fb8c1b2b1af 100644 --- a/src/style-spec/validate/validate_layer.js +++ b/src/style-spec/validate/validate_layer.js @@ -80,9 +80,13 @@ export default function validateLayer(options: Options): Array errors.push(new ValidationError(key, layer, `layer "${layer.id}" must specify a "source-layer"`)); } else if (sourceType === 'raster-dem' && type !== 'hillshade') { errors.push(new ValidationError(key, layer.source, 'raster-dem source can only be used with layer type \'hillshade\'.')); + } else if (sourceType === 'raster-array' && !['raster', 'raster-particle'].includes(type)) { + errors.push(new ValidationError(key, layer.source, `raster-array source can only be used with layer type \'raster\'.`)); } else if (type === 'line' && layer.paint && (layer.paint['line-gradient'] || layer.paint['line-trim-offset']) && (sourceType !== 'geojson' || !source.lineMetrics)) { errors.push(new ValidationError(key, layer, `layer "${layer.id}" specifies a line-gradient, which requires a GeoJSON source with \`lineMetrics\` enabled.`)); + } else if (type === 'raster-particle' && sourceType !== 'raster-array') { + errors.push(new ValidationError(key, layer.source, `layer "${layer.id}" requires a \'raster-array\' source.`)); } } } diff --git a/src/style-spec/validate/validate_model.js b/src/style-spec/validate/validate_model.js index 6da096b84e5..1fa63310fd1 100644 --- a/src/style-spec/validate/validate_model.js +++ b/src/style-spec/validate/validate_model.js @@ -6,10 +6,10 @@ import getType from '../util/get_type.js'; import type {ValidationOptions} from './validate.js'; // Allow any URL, use dummy base, if it's a relative URL -function isValidUrl(str: string): boolean { +export function isValidUrl(str: string, allowRelativeUrls: boolean): boolean { const isRelative = str.indexOf('://') === -1; try { - new URL(str, isRelative ? 'http://example.com' : undefined); + new URL(str, isRelative && allowRelativeUrls ? 'http://example.com' : undefined); return true; } catch (_) { return false; @@ -30,7 +30,7 @@ export default function validateModel(options: ValidationOptions): Array } export default function validateStyle(style: StyleSpecification, styleSpec: Object = latestStyleSpec, options: StyleValidationOptions = {}): ValidationError[] { diff --git a/src/style-spec/validate/validate_terrain.js b/src/style-spec/validate/validate_terrain.js index 330e6179702..644a977ba49 100644 --- a/src/style-spec/validate/validate_terrain.js +++ b/src/style-spec/validate/validate_terrain.js @@ -18,6 +18,8 @@ export default function validateTerrain(options: ValidationOptions): Array, path: ?string): Array< return errors; } -const acceptedSourceTypes = new Set(["vector", "raster", "raster-dem", "model", "batched-model"]); +const acceptedSourceTypes = new Set(["vector", "raster", "raster-dem", "raster-array", "model", "batched-model"]); function getSourceErrors(source: Object, i: number): Array { const errors = []; @@ -48,7 +48,7 @@ function getSourceErrors(source: Object, i: number): Array { errors.push(...getAllowedKeyErrors(source, sourceKeys, 'source')); /* - * "type" is required and must be one of "vector", "raster", "raster-dem" + * "type" is required and must be one of "vector", "raster", "raster-dem", "raster-array" */ if (!acceptedSourceTypes.has(String(source.type))) { errors.push(new ValidationError(`sources[${i}].type`, source.type, `Expected one of [${Array.from(acceptedSourceTypes).join(", ")}]`)); diff --git a/src/style/create_style_layer.js b/src/style/create_style_layer.js index 40d795f2449..dc69864c882 100644 --- a/src/style/create_style_layer.js +++ b/src/style/create_style_layer.js @@ -10,6 +10,7 @@ import line from './style_layer/line_style_layer.js'; import symbol from './style_layer/symbol_style_layer.js'; import background from './style_layer/background_style_layer.js'; import raster from './style_layer/raster_style_layer.js'; +import rasterParticle from './style_layer/raster_particle_style_layer.js'; import CustomStyleLayer from './style_layer/custom_style_layer.js'; import sky from './style_layer/sky_style_layer.js'; import slot from './style_layer/slot_style_layer.js'; @@ -17,7 +18,7 @@ import type {CustomLayerInterface} from './style_layer/custom_style_layer.js'; import model from '../../3d-style/style/style_layer/model_style_layer.js'; import type {LayerSpecification} from '../style-spec/types.js'; -import type {Expression} from '../style-spec/expression/expression.js'; +import type {ConfigOptions} from './properties.js'; const subclasses = { circle, @@ -29,15 +30,16 @@ const subclasses = { symbol, background, raster, + 'raster-particle': rasterParticle, sky, slot, model }; -export default function createStyleLayer(layer: LayerSpecification | CustomLayerInterface, options?: ?Map): StyleLayer | CustomStyleLayer { +export default function createStyleLayer(layer: LayerSpecification | CustomLayerInterface, scope: string, options?: ?ConfigOptions): StyleLayer | CustomStyleLayer { if (layer.type === 'custom') { - return new CustomStyleLayer(layer); + return new CustomStyleLayer(layer, scope); } else { - return new subclasses[layer.type](layer, options); + return new subclasses[layer.type](layer, scope, options); } } diff --git a/src/style/fog.js b/src/style/fog.js index d0a9369b397..8e2f101a41c 100644 --- a/src/style/fog.js +++ b/src/style/fog.js @@ -7,19 +7,20 @@ import {validateStyle, validateFog, emitValidationErrors} from './validate_style import {Properties, Transitionable, Transitioning, PossiblyEvaluated, DataConstantProperty} from './properties.js'; import Color from '../style-spec/util/color.js'; import {FOG_PITCH_START, FOG_PITCH_END, FOG_OPACITY_THRESHOLD, getFogOpacityAtLngLat, getFogOpacityAtMercCoord, getFovAdjustedFogRange, getFogOpacityForBounds} from './fog_helpers.js'; +import {number as interpolate, array as vecInterpolate} from '../style-spec/util/interpolate.js'; +import {globeToMercatorTransition} from '../geo/projection/globe_util.js'; +import {Frustum} from '../util/primitives.js'; +import {OverscaledTileID} from '../source/tile_id.js'; +import EXTENT from '../style-spec/data/extent.js'; + import type {FogSpecification} from '../style-spec/types.js'; import type EvaluationParameters from './evaluation_parameters.js'; -import type {TransitionParameters} from './properties.js'; +import type {TransitionParameters, ConfigOptions} from './properties.js'; import type LngLat from '../geo/lng_lat.js'; import type Transform from '../geo/transform.js'; import type {StyleSetterOptions} from '../style/style.js'; import type {FogState} from './fog_helpers.js'; import type {Mat4, Vec3} from 'gl-matrix'; -import {number as interpolate, array as vecInterpolate} from '../style-spec/util/interpolate.js'; -import {globeToMercatorTransition} from '../geo/projection/globe_util.js'; -import {Frustum} from '../util/primitives.js'; -import {OverscaledTileID} from '../source/tile_id.js'; -import EXTENT from '../style-spec/data/extent.js'; type Props = {| "range": DataConstantProperty<[number, number]>, @@ -45,17 +46,19 @@ class Fog extends Evented { _transitionable: Transitionable; _transitioning: Transitioning; properties: PossiblyEvaluated; + _options: FogSpecification; // Alternate projections do not yet support fog. // Hold on to transform so that we know whether a projection is set. _transform: Transform; - constructor(fogOptions?: FogSpecification, transform: Transform) { + constructor(fogOptions?: FogSpecification, transform: Transform, scope: string, configOptions?: ?ConfigOptions) { super(); - this._transitionable = new Transitionable(fogProperties); - this.set(fogOptions); + this._transitionable = new Transitionable(fogProperties, scope, new Map(configOptions)); + this.set(fogOptions, configOptions); this._transitioning = this._transitionable.untransitioned(); this._transform = transform; + this.properties = new PossiblyEvaluated(fogProperties); } get state(): FogState { @@ -78,7 +81,7 @@ class Fog extends Evented { return (this._transitionable.serialize(): any); } - set(fog?: FogSpecification, options: StyleSetterOptions = {}) { + set(fog?: FogSpecification, configOptions?: ?ConfigOptions, options: StyleSetterOptions = {}) { if (this._validate(validateFog, fog, options)) { return; } @@ -91,7 +94,8 @@ class Fog extends Evented { } } - this._transitionable.setTransitionOrValue(properties); + this._options = properties; + this._transitionable.setTransitionOrValue(this._options, configOptions); } getOpacity(pitch: number): number { @@ -156,6 +160,10 @@ class Fog extends Evented { return false; } + updateConfig(configOptions?: ?ConfigOptions) { + this._transitionable.setTransitionOrValue(this._options, new Map(configOptions)); + } + updateTransitions(parameters: TransitionParameters) { this._transitioning = this._transitionable.transitioned(parameters, this._transitioning); } diff --git a/src/style/properties.js b/src/style/properties.js index 6a68ee82bae..e99e2be6589 100644 --- a/src/style/properties.js +++ b/src/style/properties.js @@ -24,6 +24,7 @@ import type { CompositeExpression } from '../style-spec/expression/index.js'; import type {Expression} from '../style-spec/expression/expression.js'; +import type {ObjMap} from '../types/obj-map.js'; type TimePoint = number; @@ -67,6 +68,17 @@ export interface Property { interpolate(a: R, b: R, t: number): R; } +export type ConfigOptionValue = { + default: Expression; + value?: Expression; + values?: Array; + minValue?: number; + maxValue?: number; + stepValue?: number; + type?: 'string' | 'number' | 'boolean' | 'color'; +}; +export type ConfigOptions = Map; + /** * `PropertyValue` represents the value part of a property key-value unit. It's used to represent both * paint and layout property values, and regardless of whether or not their property supports data-driven @@ -91,10 +103,10 @@ export class PropertyValue { value: PropertyValueSpecification | void; expression: StylePropertyExpression; - constructor(property: Property, value: PropertyValueSpecification | void, options?: ?Map) { + constructor(property: Property, value: PropertyValueSpecification | void, scope?: ?string, options?: ?ConfigOptions) { this.property = property; this.value = value; - this.expression = normalizePropertyExpression(value === undefined ? property.specification.default : value, property.specification, options); + this.expression = normalizePropertyExpression(value === undefined ? property.specification.default : value, property.specification, scope, options); } isDataDriven(): boolean { @@ -130,9 +142,9 @@ class TransitionablePropertyValue { value: PropertyValue; transition: TransitionSpecification | void; - constructor(property: Property, options?: ?Map) { + constructor(property: Property, scope?: ?string, options?: ?ConfigOptions) { this.property = property; - this.value = new PropertyValue(property, undefined, options); + this.value = new PropertyValue(property, undefined, scope, options); } transitioned(parameters: TransitionParameters, @@ -152,8 +164,8 @@ class TransitionablePropertyValue { * * @private */ -type TransitionablePropertyValues - = $Exact<$ObjMap(p: Property) => TransitionablePropertyValue>> +type TransitionablePropertyValues + = $Exact(p: Property) => TransitionablePropertyValue>> /** * `Transitionable` stores a map of all (property name, `TransitionablePropertyValue`) pairs for paint properties of a @@ -162,34 +174,37 @@ type TransitionablePropertyValues * * @private */ -export class Transitionable { +export class Transitionable { _properties: Properties; _values: TransitionablePropertyValues; - _options: ?Map; + _scope: ?string; + _options: ?ConfigOptions; isConfigDependent: boolean; - constructor(properties: Properties, options?: ?Map) { + constructor(properties: Properties, scope?: ?string, options?: ?ConfigOptions) { this._properties = properties; this._values = (Object.create(properties.defaultTransitionablePropertyValues): any); + this._scope = scope; this._options = options; this.isConfigDependent = false; } - getValue(name: S): PropertyValueSpecification | void { + getValue(name: S): PropertyValueSpecification | void { + // $FlowFixMe[incompatible-return] return clone(this._values[name].value.value); } setValue(name: S, value: PropertyValueSpecification | void) { if (!this._values.hasOwnProperty(name)) { - this._values[name] = new TransitionablePropertyValue(this._values[name].property, this._options); + this._values[name] = new TransitionablePropertyValue(this._values[name].property, this._scope, this._options); } // Note that we do not _remove_ an own property in the case where a value is being reset // to the default: the transition might still be non-default. - this._values[name].value = new PropertyValue(this._values[name].property, value === null ? undefined : clone(value), this._options); + this._values[name].value = new PropertyValue(this._values[name].property, value === null ? undefined : clone(value), this._scope, this._options); this.isConfigDependent = this.isConfigDependent || this._values[name].value.expression.isConfigDependent; } - setTransitionOrValue(properties: ?P, options?: ?Map) { + setTransitionOrValue(properties: ?P, options?: ?ConfigOptions) { if (options) this._options = options; const specProperties = this._properties.properties; @@ -287,7 +302,7 @@ class TransitioningPropertyValue { } } - possiblyEvaluate(parameters: EvaluationParameters, canonical: CanonicalTileID, availableImages: Array): R { + possiblyEvaluate(parameters: EvaluationParameters, canonical: CanonicalTileID | void, availableImages: Array | void): R { const now = parameters.now || 0; const finalValue = this.value.possiblyEvaluate(parameters, canonical, availableImages); const prior = this.prior; @@ -321,8 +336,8 @@ class TransitioningPropertyValue { * * @private */ -type TransitioningPropertyValues - = $Exact<$ObjMap(p: Property) => TransitioningPropertyValue>> +type TransitioningPropertyValues + = $Exact(p: Property) => TransitioningPropertyValue>> /** * `Transitioning` stores a map of all (property name, `TransitioningPropertyValue`) pairs for paint properties of a @@ -331,7 +346,7 @@ type TransitioningPropertyValues * * @private */ -export class Transitioning { +export class Transitioning { _properties: Properties; _values: TransitioningPropertyValues; @@ -366,8 +381,8 @@ export class Transitioning { * * @private */ -type PropertyValues - = $Exact<$ObjMap(p: Property) => PropertyValue>> +type PropertyValues + = $Exact(p: Property) => PropertyValue>> /** * A helper type: given an object type `Properties` whose values are each of type `Property`, it calculates @@ -375,8 +390,8 @@ type PropertyValues * * @private */ -type PropertyValueSpecifications - = $Exact<$ObjMap(p: Property) => PropertyValueSpecification>> +type PropertyValueSpecifications + = $Exact(p: Property) => PropertyValueSpecification>> /** * Because layout properties are not transitionable, they have a simpler representation and evaluation chain than @@ -389,15 +404,17 @@ type PropertyValueSpecifications * * @private */ -export class Layout { +export class Layout { _properties: Properties; _values: PropertyValues; - _options: ?Map; + _scope: string; + _options: ?ConfigOptions; isConfigDependent: boolean; - constructor(properties: Properties, options?: ?Map) { + constructor(properties: Properties, scope: string, options?: ?ConfigOptions) { this._properties = properties; this._values = (Object.create(properties.defaultPropertyValues): any); + this._scope = scope; this._options = options; this.isConfigDependent = false; } @@ -407,12 +424,12 @@ export class Layout { } setValue(name: S, value: any) { - this._values[name] = new PropertyValue(this._values[name].property, value === null ? undefined : clone(value), this._options); + this._values[name] = new PropertyValue(this._values[name].property, value === null ? undefined : clone(value), this._scope, this._options); this.isConfigDependent = this.isConfigDependent || this._values[name].expression.isConfigDependent; } serialize(): PropertyValueSpecifications { - const result: Object = {}; + const result: any = {}; for (const property of Object.keys(this._values)) { const value = this.getValue(property); if (value !== undefined) { @@ -510,15 +527,15 @@ export class PossiblyEvaluatedPropertyValue { * * @private */ -type PossiblyEvaluatedPropertyValues - = $Exact<$ObjMap(p: Property) => R>> +type PossiblyEvaluatedPropertyValues + = $Exact(p: Property) => R>> /** * `PossiblyEvaluated` stores a map of all (property name, `R`) pairs for paint or layout properties of a * given layer type. * @private */ -export class PossiblyEvaluated { +export class PossiblyEvaluated { _properties: Properties; _values: PossiblyEvaluatedPropertyValues; @@ -571,10 +588,10 @@ export class DataConstantProperty implements Property { */ export class DataDrivenProperty implements Property> { specification: StylePropertySpecification; - overrides: ?Object; + overrides: ?{[string]: any}; useIntegerZoom: ?boolean; - constructor(specification: StylePropertySpecification, overrides?: Object) { + constructor(specification: StylePropertySpecification, overrides?: {[string]: any}) { this.specification = specification; this.overrides = overrides; } @@ -702,7 +719,7 @@ export class PositionProperty implements Property<[number, number, number], Posi * * @private */ -export class Properties { +export class Properties { properties: Props; defaultPropertyValues: PropertyValues; defaultTransitionablePropertyValues: TransitionablePropertyValues; diff --git a/src/style/query_geometry.js b/src/style/query_geometry.js index 660e82e4743..8d9443e70db 100644 --- a/src/style/query_geometry.js +++ b/src/style/query_geometry.js @@ -4,19 +4,20 @@ import Point from '@mapbox/point-geometry'; import {getBounds, clamp, polygonizeBounds, bufferConvexPolygon} from '../util/util.js'; import {polygonIntersectsBox, polygonContainsPoint} from '../util/intersection_tests.js'; import EXTENT from '../style-spec/data/extent.js'; -import type {PointLike} from '@mapbox/point-geometry'; -import type Transform from '../geo/transform.js'; -import type Tile from '../source/tile.js'; import pixelsToTileUnits from '../source/pixels_to_tile_units.js'; import {vec3, vec4, mat4} from 'gl-matrix'; import {Ray} from '../util/primitives.js'; import MercatorCoordinate, {mercatorXfromLng} from '../geo/mercator_coordinate.js'; -import type {OverscaledTileID} from '../source/tile_id.js'; import {getTilePoint, getTileVec3} from '../geo/projection/tile_transform.js'; import resample from '../geo/projection/resample.js'; -import {GLOBE_RADIUS} from '../geo/projection/globe_util.js'; +import {GLOBE_RADIUS} from '../geo/projection/globe_constants.js'; import {number as interpolate} from '../style-spec/util/interpolate.js'; +import type Transform from '../geo/transform.js'; +import type Tile from '../source/tile.js'; +import type {PointLike} from '../types/point-like.js'; +import type {OverscaledTileID} from '../source/tile_id.js'; + type CachedPolygon = { // Query rectangle projected on the map plane polygon: MercatorCoordinate[]; diff --git a/src/style/style.js b/src/style/style.js index 934bdf9fb12..cd79b910804 100644 --- a/src/style/style.js +++ b/src/style/style.js @@ -1,6 +1,7 @@ // @flow import assert from 'assert'; +import murmur3 from 'murmurhash-js'; import {Event, ErrorEvent, Evented} from '../util/evented.js'; import StyleLayer from './style_layer.js'; @@ -14,7 +15,8 @@ import Terrain, {DrapeRenderMode} from './terrain.js'; import Fog from './fog.js'; import {pick, clone, extend, deepEqual, filterObject, cartesianPositionToSpherical, warnOnce} from '../util/util.js'; import {getJSON, getReferrer, makeRequest, ResourceType} from '../util/ajax.js'; -import {isMapboxURL} from '../util/mapbox.js'; +import {isMapboxURL} from '../util/mapbox_url.js'; +import {stripQueryParameters} from '../util/url.js'; import browser from '../util/browser.js'; import Dispatcher from '../util/dispatcher.js'; import Lights from '../../3d-style/style/lights.js'; @@ -22,11 +24,10 @@ import {properties as ambientProps} from '../../3d-style/style/ambient_light_pro import {properties as directionalProps} from '../../3d-style/style/directional_light_properties.js'; import {createExpression} from '../style-spec/expression/index.js'; -import type {LightProps as Ambient} from '../../3d-style/style/ambient_light_properties.js'; -import type {LightProps as Directional} from '../../3d-style/style/directional_light_properties.js'; -import type {Vec3} from 'gl-matrix'; import { validateStyle, + validateLayoutProperty, + validatePaintProperty, validateSource, validateLayer, validateFilter, @@ -58,8 +59,10 @@ import { import PauseablePlacement from './pauseable_placement.js'; import CrossTileSymbolIndex from '../symbol/cross_tile_symbol_index.js'; import {validateCustomStyleLayer} from './style_layer/custom_style_layer.js'; -import {makeFQID, getNameFromFQID} from '../util/fqid.js'; +import {isFQID, makeFQID, getNameFromFQID, getScopeFromFQID} from '../util/fqid.js'; import {shadowDirectionFromProperties} from '../../3d-style/render/shadow_renderer.js'; +import ModelManager from '../../3d-style/render/model_manager.js'; +import {DEFAULT_MAX_ZOOM, DEFAULT_MIN_ZOOM} from '../geo/transform.js'; // We're skipping validation errors with the `source.canvas` identifier in order // to continue to allow canvas sources to be added at runtime/updated in @@ -67,7 +70,10 @@ import {shadowDirectionFromProperties} from '../../3d-style/render/shadow_render const emitValidationErrors = (evented: Evented, errors: ?ValidationErrors) => _emitValidationErrors(evented, errors && errors.filter(error => error.identifier !== 'source.canvas')); -import type {default as MapboxMap} from '../ui/map.js'; +import type {LightProps as Ambient} from '../../3d-style/style/ambient_light_properties.js'; +import type {LightProps as Directional} from '../../3d-style/style/directional_light_properties.js'; +import type {Vec3} from 'gl-matrix'; +import type {Map as MapboxMap} from '../ui/map.js'; import type Transform from '../geo/transform.js'; import type {StyleImage} from './style_image.js'; import type {StyleGlyph} from './style_glyph.js'; @@ -101,12 +107,9 @@ import type {OverscaledTileID} from '../source/tile_id.js'; import type {QueryResult} from '../data/feature_index.js'; import type {QueryFeature} from '../util/vectortile_to_geojson.js'; import type {FeatureStates} from '../source/source_state.js'; -import type {PointLike} from '@mapbox/point-geometry'; +import type {PointLike} from '../types/point-like.js'; import type {Source, SourceClass} from '../source/source.js'; -import type {TransitionParameters} from './properties.js'; -import ModelManager from '../../3d-style/render/model_manager.js'; -import type {Expression} from '../style-spec/expression/expression.js'; -import {DEFAULT_MAX_ZOOM, DEFAULT_MIN_ZOOM} from '../geo/transform.js'; +import type {TransitionParameters, ConfigOptions} from './properties.js'; import type {QueryRenderedFeaturesParams} from '../source/query_features.js'; const supportedDiffOperations = pick(diffOperations, [ @@ -129,9 +132,7 @@ const supportedDiffOperations = pick(diffOperations, [ 'setCamera', 'addImport', 'removeImport', - 'setImportUrl', - 'setImportData', - 'setImportConfig', + 'updateImport' // 'setGlyphs', // 'setSprite', ]); @@ -155,15 +156,19 @@ export type StyleOptions = { glyphManager?: GlyphManager, modelManager?: ModelManager, styleChanges?: StyleChanges, + configOptions?: ConfigOptions, scope?: string, importDepth?: number, importsCache?: Map, resolvedImports?: Set, + config?: ?ConfigSpecification, + configDependentLayers?: Set; }; export type StyleSetterOptions = { - validate?: boolean + validate?: boolean; + isInitialLoad?: boolean; }; export type Fragment = {| @@ -175,9 +180,6 @@ export type Fragment = {| const MAX_IMPORT_DEPTH = 5; const defaultTransition = {duration: 300, delay: 0}; -// Symbols are draped only on native and for certain cases only -const drapedLayers = new Set(['fill', 'line', 'background', 'hillshade', 'raster']); - /** * @private */ @@ -192,11 +194,15 @@ class Style extends Evented { directionalLight: ?Lights; light: Light; terrain: ?Terrain; + disableElevatedTerrain: ?boolean; fog: ?Fog; camera: CameraSpecification; transition: TransitionSpecification; projection: ProjectionSpecification; + // Serializable identifier of style, which we use for telemetry + globalId: string | null; + scope: string; fragments: Array; importDepth: number; @@ -205,7 +211,7 @@ class Style extends Evented { // Keeps track of ancestors' Style URLs. resolvedImports: Set; - options: Map; + options: ConfigOptions; // Merged layers and sources _mergedOrder: Array; @@ -234,6 +240,7 @@ class Style extends Evented { _markersNeedUpdate: boolean; _brightness: ?number; _configDependentLayers: Set; + _config: ?ConfigSpecification; _buildingIndex: BuildingIndex; _transition: TransitionSpecification; @@ -259,6 +266,8 @@ class Style extends Evented { // Empty string indicates the root Style scope. this.scope = options.scope || ''; + this.globalId = null; + this.fragments = []; this.importDepth = options.importDepth || 0; this.importsCache = options.importsCache || new Map(); @@ -325,8 +334,9 @@ class Style extends Evented { this._order = []; this._markersNeedUpdate = false; - this.options = new Map(); - this._configDependentLayers = new Set(); + this.options = options.configOptions ? options.configOptions : new Map(); + this._configDependentLayers = options.configDependentLayers ? options.configDependentLayers : new Set(); + this._config = options.config; this.dispatcher.broadcast('setReferrer', getReferrer()); @@ -372,6 +382,72 @@ class Style extends Evented { }); } + load(style: StyleSpecification | string | null): Style { + if (!style) { + return this; + } + + if (typeof style === 'string') { + this.loadURL(style); + } else { + this.loadJSON(style); + } + + return this; + } + + _getGlobalId(loadedStyle?: StyleSpecification | string | null): string | null { + if (!loadedStyle) { + return null; + } + + if (typeof loadedStyle === 'string') { + if (isMapboxURL(loadedStyle)) { + return loadedStyle; + } + + const url = stripQueryParameters(loadedStyle); + + if (!url.startsWith('http')) { + try { + return new URL(url, location.href).toString(); + } catch (_e) { + return url; + } + } + + return url; + } + + return `json://${murmur3(JSON.stringify(loadedStyle))}`; + } + + _diffStyle(style: StyleSpecification | string, onStarted: (err: Error | null, isUpdateNeeded: boolean) => void, onFinished?: () => void) { + this.globalId = this._getGlobalId(style); + + const handleStyle = (json: StyleSpecification, callback: (err: Error | null, isUpdateNeeded: boolean) => void) => { + try { + callback(null, this.setState(json, onFinished)); + } catch (e) { + callback(e, false); + } + }; + + if (typeof style === 'string') { + const url = this.map._requestManager.normalizeStyleURL(style); + const request = this.map._requestManager.transformRequest(url, ResourceType.Style); + getJSON(request, (error: ?Error, json: ?Object) => { + if (error) { + this.fire(new ErrorEvent(error)); + } else if (json) { + handleStyle(json, onStarted); + } + }); + } else if (typeof style === 'object') { + handleStyle(style, onStarted); + } + } + loadURL(url: string, options: { validate?: boolean, accessToken?: string @@ -381,6 +457,7 @@ class Style extends Evented { const validate = typeof options.validate === 'boolean' ? options.validate : !isMapboxURL(url); + this.globalId = this._getGlobalId(url); url = this.map._requestManager.normalizeStyleURL(url, options.accessToken); this.resolvedImports.add(url); @@ -401,6 +478,8 @@ class Style extends Evented { loadJSON(json: StyleSpecification, options: StyleSetterOptions = {}): void { this.fire(new Event('dataloading', {dataType: 'style'})); + + this.globalId = this._getGlobalId(json); this._request = browser.frame(() => { this._request = null; this._load(json, options.validate !== false); @@ -412,18 +491,23 @@ class Style extends Evented { this._load(empty, false); } - _loadImports(imports: Array, validate: boolean): void { + _loadImports(imports: Array, validate: boolean, beforeId: ?string): Promise { // We take the root style into account when calculating the import depth. if (this.importDepth >= MAX_IMPORT_DEPTH - 1) { warnOnce(`Style doesn't support nesting deeper than ${MAX_IMPORT_DEPTH}`); - return this._reloadImports(); + return Promise.resolve(); } const waitForStyles = []; for (const importSpec of imports) { const style = this._createFragmentStyle(importSpec); - const waitForStyle = new Promise(resolve => style.on('style.import.load', resolve)); + // Merge everything and update layers after the import style is settled. + const waitForStyle = new Promise((resolve) => { + style.once('style.import.load', resolve); + style.once('error', resolve); + }) + .then(() => this.mergeAll()); waitForStyles.push(waitForStyle); // Load empty style if one of the ancestors was already @@ -437,6 +521,13 @@ class Style extends Evented { const json = importSpec.data || this.importsCache.get(importSpec.url); if (json) { style.loadJSON(json, {validate}); + + // Don't expose global ID for internal style to ensure + // that we don't send in telemetry Standard style as import + // because we already use it directly + if (this._isInternalStyle(json)) { + style.globalId = null; + } } else if (importSpec.url) { style.loadURL(importSpec.url, {validate}); } else { @@ -449,11 +540,34 @@ class Style extends Evented { config: importSpec.config }; - this.fragments.push(fragment); + if (beforeId) { + const beforeIndex = this.fragments.findIndex(({id}) => id === beforeId); + + assert(beforeIndex !== -1, `Import with id "${beforeId}" does not exist on this map`); + + this.fragments = this.fragments + .slice(0, beforeIndex) + .concat(fragment) + .concat(this.fragments.slice(beforeIndex)); + } else { + this.fragments.push(fragment); + } + } // $FlowFixMe[method-unbinding] - Promise.all(waitForStyles).then(() => this._reloadImports()); + return Promise.allSettled(waitForStyles); + } + + getImportGlobalIds(style: Style = this, ids: Set = new Set()): string[] { + for (const fragment of style.fragments) { + if (fragment.style.globalId) { + ids.add(fragment.style.globalId); + } + this.getImportGlobalIds(fragment.style, ids); + } + + return [...ids.values()]; } _createFragmentStyle(importSpec: ImportSpecification): Style { @@ -471,19 +585,22 @@ class Style extends Evented { imageManager: this.imageManager, glyphManager: this.glyphManager, modelManager: this.modelManager, + config: importSpec.config, + configOptions: this.options, + configDependentLayers: this._configDependentLayers }); // Bubble all events fired by the style to the map. style.setEventedParent(this.map, {style}); - style.setConfig(importSpec.config); - return style; } _reloadImports() { this.mergeAll(); this._updateMapProjection(); + this.updateConfigDependencies(); + this.map._triggerCameraUpdate(this.camera); this.dispatcher.broadcast('setLayers', { layers: this._serializeLayers(this._order), @@ -491,10 +608,11 @@ class Style extends Evented { options: this.options }); - const isRootStyle = this.isRootStyle(); - this._shouldPrecompile = isRootStyle; - this.fire(new Event('data', {dataType: 'style'})); - this.fire(new Event(isRootStyle ? 'style.load' : 'style.import.load')); + this._shouldPrecompile = this.isRootStyle(); + } + + _isInternalStyle(json: StyleSpecification): boolean { + return this.isRootStyle() && (json.fragment || (!!json.schema && json.fragment !== false)); } _load(json: StyleSpecification, validate: boolean) { @@ -502,14 +620,14 @@ class Style extends Evented { // This style was loaded as a root style, but it is marked as a fragment and/or has a schema. We instead load // it as an import with the well-known ID "basemap" to make sure that we don't expose the internals. - if (this.isRootStyle() && (json.fragment || (schema && json.fragment !== false))) { + if (this._isInternalStyle(json)) { const basemap = {id: 'basemap', data: json, url: ''}; const style = extend({}, empty, {imports: [basemap]}); this._load(style, validate); return; } - this.updateSchema(schema); + this.setConfig(this._config, schema); if (validate && emitValidationErrors(this, validateStyle(json))) { return; @@ -519,9 +637,8 @@ class Style extends Evented { this.stylesheet = clone(json); for (const id in json.sources) { - this.addSource(id, json.sources[id], {validate: false}); + this.addSource(id, json.sources[id], {validate: false, isInitialLoad: true}); } - this._changes.changed = false; // avoid triggering redundant style update after adding initial sources if (json.sprite) { this._loadSprite(json.sprite); @@ -555,12 +672,10 @@ class Style extends Evented { this._layers = {}; this._serializedLayers = {}; for (const layer of layers) { - const styleLayer = createStyleLayer(layer, this.options); - styleLayer.setScope(this.scope); + const styleLayer = createStyleLayer(layer, this.scope, this.options); if (styleLayer.isConfigDependent) this._configDependentLayers.add(styleLayer.fqid); styleLayer.setEventedParent(this, {layer: {id: styleLayer.id}}); this._layers[styleLayer.id] = styleLayer; - this._layers[styleLayer.id] = styleLayer; this._serializedLayers[styleLayer.id] = styleLayer.serialize(); const sourceCache = this.getOwnLayerSourceCache(styleLayer); @@ -576,8 +691,17 @@ class Style extends Evented { } const terrain = this.stylesheet.terrain; - if (terrain && !this.terrainSetForDrapingOnly()) { - this._createTerrain(terrain, DrapeRenderMode.elevated); + if (terrain) { + // This workaround disables terrain and hillshade + // if there is noise in the Canvas2D operations used for image decoding. + if (this.disableElevatedTerrain === undefined) + this.disableElevatedTerrain = browser.hasCanvasFingerprintNoise(); + + if (this.disableElevatedTerrain) { + warnOnce('Terrain and hillshade are disabled because of Canvas2D limitations when fingerprinting protection is enabled (e.g. in private browsing mode).'); + } else if (!this.terrainSetForDrapingOnly()) { + this._createTerrain(terrain, DrapeRenderMode.elevated); + } } if (this.stylesheet.fog) { @@ -588,25 +712,17 @@ class Style extends Evented { this.setTransition(this.stylesheet.transition); } - this.mergeAll(); - this._updateMapProjection(); - - // Trigger update - this.map._triggerCameraUpdate(this.camera); - this.fire(new Event('data', {dataType: 'style'})); + const isRootStyle = this.isRootStyle(); + if (json.imports) { - this._loadImports(json.imports, validate); - } else { - this.dispatcher.broadcast('setLayers', { - layers: this._serializeLayers(this._order), - scope: this.scope, - options: this.options + this._loadImports(json.imports, validate).then(() => { + this._reloadImports(); + this.fire(new Event(isRootStyle ? 'style.load' : 'style.import.load')); }); - - const isRootStyle = this.isRootStyle(); - this._shouldPrecompile = isRootStyle; + } else { + this._reloadImports(); this.fire(new Event(isRootStyle ? 'style.load' : 'style.import.load')); } } @@ -625,6 +741,11 @@ class Style extends Evented { let transition; let camera; + // Reset terrain that might have been set by a previous merge + if (this.terrain && this.terrain.scope !== this.scope) { + delete this.terrain; + } + this.forEachFragmentStyle((style: Style) => { if (!style.stylesheet) return; @@ -641,14 +762,11 @@ class Style extends Evented { } } - const isGlobe = style.stylesheet.projection && style.stylesheet.projection.name === 'globe'; - if ((style.stylesheet.terrain || isGlobe) && style.terrain != null) { - const nextIsElevated = style.terrain.drapeRenderMode === DrapeRenderMode.elevated; - const prevIsDeffered = terrain && terrain.drapeRenderMode === DrapeRenderMode.deferred; - if (!terrain || prevIsDeffered || nextIsElevated) { - terrain = style.terrain; - } - } + terrain = this._prioritizeTerrain( + terrain, + style.terrain, + style.stylesheet.terrain, + ); if (style.stylesheet.fog && style.fog != null) fog = style.fog; @@ -667,9 +785,14 @@ class Style extends Evented { this.light = light; this.ambientLight = ambientLight; this.directionalLight = directionalLight; - this.terrain = terrain; this.fog = fog; + if (terrain === null) { + delete this.terrain; + } else { + this.terrain = terrain; + } + // Use perspective camera as a fallback if no camera is specified this.camera = camera || {'camera-projection': 'perspective'}; this.projection = projection || {name: 'mercator'}; @@ -691,20 +814,55 @@ class Style extends Evented { traverse(this); } + _prioritizeTerrain(prevTerrain: ?Terrain, nextTerrain: ?Terrain, nextTerrainSpec: ?TerrainSpecification): ?Terrain { + // Given the previous and next terrain during imports merging, in order of priority, we select: + // 1. null, if the next terrain is explicitly disabled and we are not using the globe + // 2. next terrain if it is not null + // 3. previous terrain + + const prevIsDeffered = prevTerrain && prevTerrain.drapeRenderMode === DrapeRenderMode.deferred; + const nextIsDeffered = nextTerrain && nextTerrain.drapeRenderMode === DrapeRenderMode.deferred; + + // Disable terrain if it was explicitly set to null and we are not using globe + if (nextTerrainSpec === null) { + // First, check if the terrain is deferred + // If so, we are using the globe and should keep the terrain + if (nextIsDeffered) return nextTerrain; + if (prevIsDeffered) return prevTerrain; + + return null; + } + + // Use next terrain if there is no previous terrain or if it is deferred + if (nextTerrain != null) { + const nextIsElevated = nextTerrain && nextTerrain.drapeRenderMode === DrapeRenderMode.elevated; + if (!prevTerrain || prevIsDeffered || nextIsElevated) return nextTerrain; + } + + return prevTerrain; + } + mergeTerrain() { let terrain; + // Reset terrain that might have been set by a previous merge + if (this.terrain && this.terrain.scope !== this.scope) { + delete this.terrain; + } + this.forEachFragmentStyle((style: Style) => { - if (style.terrain != null) { - const nextIsElevated = style.terrain.drapeRenderMode === DrapeRenderMode.elevated; - const prevIsDeffered = terrain && terrain.drapeRenderMode === DrapeRenderMode.deferred; - if (!terrain || prevIsDeffered || nextIsElevated) { - terrain = style.terrain; - } - } + terrain = this._prioritizeTerrain( + terrain, + style.terrain, + style.stylesheet.terrain, + ); }); - this.terrain = terrain; + if (terrain === null) { + delete this.terrain; + } else { + this.terrain = terrain; + } } mergeProjection() { @@ -826,12 +984,12 @@ class Style extends Evented { this.dispatcher.broadcast('setProjection', this.map.transform.projectionOptions); if (this.map.transform.projection.requiresDraping) { - const hasTerrain = this.getTerrain() || this.stylesheet.terrain; + const hasTerrain = (this.getTerrain() || this.stylesheet.terrain) && !this.disableElevatedTerrain; if (!hasTerrain) { this.setTerrainForDraping(); } } else if (this.terrainSetForDrapingOnly()) { - this.setTerrain(null); + this.setTerrain(null, DrapeRenderMode.deferred); } } @@ -892,7 +1050,7 @@ class Style extends Evented { if (!this._loaded) return false; - if (Object.keys(this._changes.updatedSourceCaches).length) + if (Object.keys(this._changes.getUpdatedSourceCaches()).length) return false; for (const id in this._sourceCaches) @@ -1004,8 +1162,7 @@ class Style extends Evented { isLayerDraped(layer: StyleLayer): boolean { if (!this.terrain) return false; - if (typeof layer.isLayerDraped === 'function') return layer.isLayerDraped(this.getLayerSourceCache(layer)); - return drapedLayers.has(layer.type); + return layer.isDraped(this.getLayerSourceCache(layer)); } _checkLoaded(): void { @@ -1056,9 +1213,8 @@ class Style extends Evented { this.dispatcher.broadcast('setBrightness', brightness); } - const changed = this._changes.changed; - - if (this._changes.changed) { + const changed = this._changes.isDirty(); + if (this._changes.isDirty()) { const updatesByScope = this._changes.getLayerUpdatesByScope(); for (const scope in updatesByScope) { const {updatedIds, removedIds} = updatesByScope[scope]; @@ -1097,14 +1253,19 @@ class Style extends Evented { const sourceCache = this._mergedSourceCaches[sourceId]; sourcesUsedBefore[sourceId] = sourceCache.used; sourceCache.used = false; + sourceCache.tileCoverLift = 0.0; } for (const layerId of this._mergedOrder) { const layer = this._mergedLayers[layerId]; layer.recalculate(parameters, this._availableImages); if (!layer.isHidden(parameters.zoom)) { - const sourceCache = this.getOwnLayerSourceCache(layer); - if (sourceCache) sourceCache.used = true; + const sourceCache = this.getLayerSourceCache(layer); + if (sourceCache) { + sourceCache.used = true; + // Select the highest elevation across all layers that are rendered with this source + sourceCache.tileCoverLift = Math.max(sourceCache.tileCoverLift, layer.tileCoverLift()); + } } if (!this._precompileDone && this._shouldPrecompile) { @@ -1170,27 +1331,25 @@ class Style extends Evented { if (changed) { this.fire(new Event('data', {dataType: 'style'})); } - - for (const {style} of this.fragments) { - style.update(parameters); - } } /* * Apply any queued image changes. */ _updateTilesForChangedImages() { - const changedImages = Array.from(this._changes.changedImages.keys()); - if (changedImages.length) { + const updatedImages = this._changes.getUpdatedImages(); + if (updatedImages.length) { for (const name in this._sourceCaches) { - this._sourceCaches[name].reloadTilesForDependencies(['icons', 'patterns'], changedImages); + this._sourceCaches[name].reloadTilesForDependencies(['icons', 'patterns'], updatedImages); } - this._changes.changedImages.clear(); + this._changes.resetUpdatedImages(); } } _updateWorkerLayers(scope: string, updatedIds?: Array, removedIds?: Array) { const fragmentStyle = this.getFragmentStyle(scope); + if (!fragmentStyle) return; + this.dispatcher.broadcast('updateLayers', { layers: updatedIds ? fragmentStyle._serializeLayers(updatedIds) : [], scope, @@ -1209,7 +1368,7 @@ class Style extends Evented { * @returns {boolean} true if any changes were made; false otherwise * @private */ - setState(nextState: StyleSpecification): boolean { + setState(nextState: StyleSpecification, onFinish?: () => void): boolean { this._checkLoaded(); if (emitValidationErrors(this, validateStyle(nextState))) return false; @@ -1229,10 +1388,16 @@ class Style extends Evented { throw new Error(`Unimplemented: ${unimplementedOps.map(op => op.command).join(', ')}.`); } + const changesPromises = []; + changes.forEach((op) => { - (this: any)[op.command].apply(this, op.args); + changesPromises.push((this: any)[op.command].apply(this, op.args)); }); + if (onFinish) { + Promise.all(changesPromises).then(onFinish); + } + this.stylesheet = nextState; this.mergeAll(); @@ -1273,8 +1438,7 @@ class Style extends Evented { _afterImageUpdated(id: string) { this._availableImages = this.imageManager.listImages(this.scope); - this._changes.changedImages.add(id); - this._changes.changed = true; + this._changes.updateImage(id); this.dispatcher.broadcast('setImages', { scope: this.scope, images: this._availableImages @@ -1292,7 +1456,7 @@ class Style extends Evented { if (this._validate(validateModel, `models.${id}`, url, null, options)) return this; this.modelManager.addModel(id, url, this.scope); - this._changes.changed = true; + this._changes.setDirty(); return this; } @@ -1329,7 +1493,6 @@ class Style extends Evented { if (shouldValidate && this._validate(validateSource, `sources.${id}`, source, null, options)) return; if (this.map && this.map._collectResourceTiming) (source: any).collectResourceTiming = true; - const sourceInstance = createSource(id, source, this.dispatcher, this); sourceInstance.scope = this.scope; @@ -1353,9 +1516,12 @@ class Style extends Evented { } if (sourceInstance.onAdd) sourceInstance.onAdd(this.map); - this.mergeSources(); - this._changes.changed = true; + // Avoid triggering redundant style update after adding initial sources. + if (!options.isInitialLoad) { + this.mergeSources(); + this._changes.setDirty(); + } } /** @@ -1376,7 +1542,7 @@ class Style extends Evented { return this.fire(new ErrorEvent(new Error(`Source "${id}" cannot be removed while layer "${layerId}" is using it.`))); } } - if (this.terrain && this.terrain.get().source === id) { + if (this.terrain && this.terrain.scope === this.scope && this.terrain.get().source === id) { return this.fire(new ErrorEvent(new Error(`Source "${id}" cannot be removed while terrain is using it.`))); } @@ -1384,7 +1550,7 @@ class Style extends Evented { for (const sourceCache of sourceCaches) { const id = getNameFromFQID(sourceCache.id); delete this._sourceCaches[id]; - delete this._changes.updatedSourceCaches[sourceCache.id]; + this._changes.discardSourceCacheUpdate(sourceCache.id); sourceCache.fire(new Event('data', {sourceDataType: 'metadata', dataType:'source', sourceId: sourceCache.getSource().id})); sourceCache.setEventedParent(null); sourceCache.clearTiles(); @@ -1397,7 +1563,7 @@ class Style extends Evented { if (source.onRemove) { source.onRemove(this.map); } - this._changes.changed = true; + this._changes.setDirty(); return this; } @@ -1414,7 +1580,7 @@ class Style extends Evented { assert(geojsonSource.type === 'geojson'); geojsonSource.setData(data); - this._changes.changed = true; + this._changes.setDirty(); } /** @@ -1437,6 +1603,19 @@ class Style extends Evented { return sources; } + areTilesLoaded(): boolean { + const sources = this._mergedSourceCaches; + for (const id in sources) { + const source = sources[id]; + const tiles = source._tiles; + for (const t in tiles) { + const tile = tiles[t]; + if (!(tile.state === 'loaded' || tile.state === 'errored')) return false; + } + } + return true; + } + setLights(lights: ?Array) { this._checkLoaded(); @@ -1542,13 +1721,29 @@ class Style extends Evented { return !!this.ambientLight && !!this.directionalLight; } - getFragmentStyle(fragmentId?: string): Style { + getFragmentStyle(fragmentId?: string): ?Style { if (!fragmentId) return this; - const fragment = this.fragments.find(({id}) => id === fragmentId); - if (!fragment) throw new Error(`Style import not found: ${fragmentId}`); + if (isFQID(fragmentId)) { + const scope = getScopeFromFQID(fragmentId); + const fragment = this.fragments.find(({id}) => id === scope); + if (!fragment) throw new Error(`Style import not found: ${fragmentId}`); + const name = getNameFromFQID(fragmentId); + return fragment.style.getFragmentStyle(name); + } else { + const fragment = this.fragments.find(({id}) => id === fragmentId); + if (!fragment) throw new Error(`Style import not found: ${fragmentId}`); + return fragment.style; + } + } - return fragment.style; + getConfigProperty(fragmentId: string, key: string): ?any { + const fragmentStyle = this.getFragmentStyle(fragmentId); + if (!fragmentStyle) return null; + const fqid = makeFQID(key, fragmentStyle.scope); + const expressions = fragmentStyle.options.get(fqid); + const expression = expressions ? expressions.value || expressions.default : null; + return expression ? expression.serialize() : null; } setConfigProperty(fragmentId: string, key: string, value: any) { @@ -1561,35 +1756,54 @@ class Style extends Evented { const expression = expressionParsed.value.expression; const fragmentStyle = this.getFragmentStyle(fragmentId); - fragmentStyle.options.set(key, expression); - fragmentStyle.updateConfigDependencies(); + if (!fragmentStyle) return; + + const fqid = makeFQID(key, fragmentStyle.scope); + const expressions = fragmentStyle.options.get(fqid); + if (!expressions) return; + + this.options.set(fqid, {...expressions, value: expression}); + this.updateConfigDependencies(); } setConfig(config: ?ConfigSpecification, schema: ?SchemaSpecification) { - this.options.clear(); - if (!config) return; + this._config = config; - for (const key in config) { - const expressionParsed = createExpression(config[key]); - if (expressionParsed.result === 'success') { - this.options.set(key, expressionParsed.value.expression); - } - } + if (!config && !schema) return; - this.updateSchema(schema); - } - - updateSchema(schema: ?SchemaSpecification) { - if (!schema) return; + if (!schema) { + this.fire(new ErrorEvent(new Error(`Attempting to set config for a style without schema.`))); + return; + } for (const id in schema) { - // already set by config - if (this.options.has(id)) continue; + let defaultExpression; + let configExpression; const expression = schema[id].default; const expressionParsed = createExpression(expression); if (expressionParsed.result === 'success') { - this.options.set(id, expressionParsed.value.expression); + defaultExpression = expressionParsed.value.expression; + } + + if (config && config[id] !== undefined) { + const expressionParsed = createExpression(config[id]); + if (expressionParsed.result === 'success') { + configExpression = expressionParsed.value.expression; + } + } + + const {minValue, maxValue, stepValue, type, values} = schema[id]; + + if (defaultExpression) { + const fqid = makeFQID(id, this.scope); + this.options.set(fqid, { + default: defaultExpression, + value: configExpression, + minValue, maxValue, stepValue, type, values + }); + } else { + this.fire(new ErrorEvent(new Error(`No schema defined for config option "${id}".`))); } } } @@ -1603,17 +1817,19 @@ class Style extends Evented { } } - // If the root style uses the lights from the updated fragment, - // update the configs in the corresponding light instances. - if (this.ambientLight && this.ambientLight.scope === this.scope) { + if (this.ambientLight) { this.ambientLight.updateConfig(this.options); } - if (this.directionalLight && this.directionalLight.scope === this.scope) { + if (this.directionalLight) { this.directionalLight.updateConfig(this.options); } - this._changes.changed = true; + if (this.fog) { + this.fog.updateConfig(this.options); + } + + this._changes.setDirty(); } /** @@ -1637,7 +1853,7 @@ class Style extends Evented { let layer; if (layerObject.type === 'custom') { if (emitValidationErrors(this, validateCustomStyleLayer(layerObject))) return; - layer = createStyleLayer(layerObject, this.options); + layer = createStyleLayer(layerObject, this.scope, this.options); } else { if (typeof layerObject.source === 'object') { this.addSource(id, layerObject.source); @@ -1649,7 +1865,7 @@ class Style extends Evented { if (this._validate(validateLayer, `layers.${id}`, layerObject, {arrayIndex: -1}, options)) return; - layer = createStyleLayer(layerObject, this.options); + layer = createStyleLayer(layerObject, this.scope, this.options); this._validateLayer(layer); layer.setEventedParent(this, {layer: {id}}); @@ -1657,12 +1873,21 @@ class Style extends Evented { } if (layer.isConfigDependent) this._configDependentLayers.add(layer.fqid); - layer.setScope(this.scope); - const index = before ? this._order.indexOf(before) : this._order.length; - if (before && index === -1) { - this.fire(new ErrorEvent(new Error(`Layer with id "${before}" does not exist on this map.`))); - return; + let index = this._order.length; + if (before) { + const beforeIndex = this._order.indexOf(before); + if (beforeIndex === -1) { + this.fire(new ErrorEvent(new Error(`Layer with id "${before}" does not exist on this map.`))); + return; + } + + // If the layer we're inserting doesn't have a slot, + // or it has the same slot as the 'before' layer, + // then we can insert the new layer before the existing one. + const beforeLayer = this._layers[before]; + if (layer.slot === beforeLayer.slot) index = beforeIndex; + else warnOnce(`Layer with id "${before}" has a different slot. Layers can only be rearranged within the same slot.`); } this._order.splice(index, 0, id); @@ -1689,9 +1914,9 @@ class Style extends Evented { this._changes.discardLayerRemoval(layer); const fqid = makeFQID(layer.source, layer.scope); if (removedLayer.type !== layer.type) { - this._changes.updatedSourceCaches[fqid] = 'clear'; + this._changes.updateSourceCache(fqid, 'clear'); } else { - this._changes.updatedSourceCaches[fqid] = 'reload'; + this._changes.updateSourceCache(fqid, 'reload'); sourceCache.pause(); } } @@ -1716,7 +1941,6 @@ class Style extends Evented { */ moveLayer(id: string, before?: string) { this._checkLoaded(); - this._changes.changed = true; const layer = this._checkLayer(id); if (!layer) return; @@ -1728,13 +1952,25 @@ class Style extends Evented { const index = this._order.indexOf(id); this._order.splice(index, 1); - const newIndex = before ? this._order.indexOf(before) : this._order.length; - if (before && newIndex === -1) { - this.fire(new ErrorEvent(new Error(`Layer with id "${before}" does not exist on this map.`))); - return; + let newIndex = this._order.length; + if (before) { + const beforeIndex = this._order.indexOf(before); + if (beforeIndex === -1) { + this.fire(new ErrorEvent(new Error(`Layer with id "${before}" does not exist on this map.`))); + return; + } + + // If the layer we're moving doesn't have a slot, + // or it has the same slot as the 'before' layer, + // then we can insert the new layer before the existing one. + const beforeLayer = this._layers[before]; + if (layer.slot === beforeLayer.slot) newIndex = beforeIndex; + else warnOnce(`Layer with id "${before}" has a different slot. Layers can only be rearranged within the same slot.`); } + this._order.splice(newIndex, 0, id); + this._changes.setDirty(); this._layerOrderChanged = true; this.mergeLayers(); @@ -1762,7 +1998,7 @@ class Style extends Evented { delete this._layers[id]; delete this._serializedLayers[id]; - this._changes.changed = true; + this._changes.setDirty(); this._layerOrderChanged = true; this._configDependentLayers.delete(layer.fqid); @@ -1782,6 +2018,7 @@ class Style extends Evented { sourceCache.castsShadows = shadowCastersLeft; } + // $FlowFixMe[method-unbinding] if (layer.onRemove) { layer.onRemove(this.map); } @@ -1891,7 +2128,7 @@ class Style extends Evented { return clone(layer.filter); } - setLayoutProperty(layerId: string, name: string, value: any, options: StyleSetterOptions = {}) { + setLayoutProperty(layerId: string, name: string, value: any, options: StyleSetterOptions = {}) { this._checkLoaded(); const layer = this._checkLayer(layerId); @@ -1899,7 +2136,23 @@ class Style extends Evented { if (deepEqual(layer.getLayoutProperty(name), value)) return; - layer.setLayoutProperty(name, value, options); + if (value !== null && value !== undefined && !(options && options.validate === false)) { + const key = `layers.${layerId}.layout.${name}`; + const errors = emitValidationErrors(layer, validateLayoutProperty.call(validateStyle, { + key, + layerType: layer.type, + objectKey: name, + value, + styleSpec, + // Workaround for https://github.com/mapbox/mapbox-gl-js/issues/2407 + style: {glyphs: true, sprite: true} + })); + if (errors) { + return; + } + } + + layer.setLayoutProperty(name, value); if (layer.isConfigDependent) this._configDependentLayers.add(layer.fqid); this._updateLayer(layer); } @@ -1924,14 +2177,27 @@ class Style extends Evented { if (deepEqual(layer.getPaintProperty(name), value)) return; - const requiresRelayout = layer.setPaintProperty(name, value, options); + if (value !== null && value !== undefined && !(options && options.validate === false)) { + const key = `layers.${layerId}.paint.${name}`; + const errors = emitValidationErrors(layer, validatePaintProperty.call(validateStyle, { + key, + layerType: layer.type, + objectKey: name, + value, + styleSpec + })); + if (errors) { + return; + } + } + + const requiresRelayout = layer.setPaintProperty(name, value); if (layer.isConfigDependent) this._configDependentLayers.add(layer.fqid); if (requiresRelayout) { this._updateLayer(layer); } - this._changes.changed = true; - this._changes.updatedPaintProps.add(layer.fqid); + this._changes.updatePaintProperties(layer); } getPaintProperty(layerId: string, name: string): void | TransitionSpecification | PropertyValueSpecification { @@ -2028,7 +2294,9 @@ class Style extends Evented { this._checkLoaded(); const terrain = this.getTerrain(); - const scopedTerrain = terrain && this.terrain && this.terrain.scope === this.scope ? terrain : undefined; + const scopedTerrain = terrain && this.terrain && this.terrain.scope === this.scope ? + terrain : + this.stylesheet.terrain; return filterObject({ version: this.stylesheet.version, @@ -2058,14 +2326,13 @@ class Style extends Evented { this._changes.updateLayer(layer); const sourceCache = this.getLayerSourceCache(layer); const fqid = makeFQID(layer.source, layer.scope); - if (layer.source && !this._changes.updatedSourceCaches[fqid] && - //Skip for raster layers (https://github.com/mapbox/mapbox-gl-js/issues/7865) - sourceCache && - sourceCache.getSource().type !== 'raster') { - this._changes.updatedSourceCaches[fqid] = 'reload'; + const sourceCacheUpdates = this._changes.getUpdatedSourceCaches(); + if (layer.source && !sourceCacheUpdates[fqid] && + // Skip for raster layers (https://github.com/mapbox/mapbox-gl-js/issues/7865) + sourceCache && sourceCache.getSource().type !== 'raster') { + this._changes.updateSourceCache(fqid, 'reload'); sourceCache.pause(); } - this._changes.changed = true; layer.invalidateCompiledFilter(); } @@ -2089,7 +2356,7 @@ class Style extends Evented { // This means that that the line_layer feature is above the extrusion_layer_b feature despite // it being in an earlier layer. - const isLayer3D = (layerId: string) => this._mergedLayers[layerId].type === 'fill-extrusion'; + const isLayer3D = (layerId: string) => this._mergedLayers[layerId].type === 'fill-extrusion' || this._mergedLayers[layerId].type === 'model'; const order = this.order; @@ -2286,14 +2553,24 @@ class Style extends Evented { // Disabling if (!terrainOptions) { - delete this.terrain; - delete this.stylesheet.terrain; + // This check prevents removing draping terrain not from #applyProjectionUpdate + if (!this.terrainSetForDrapingOnly() || drapeRenderMode === DrapeRenderMode.deferred) { + delete this.terrain; + } + + if (terrainOptions === null) { + this.stylesheet.terrain = null; + } else { + delete this.stylesheet.terrain; + } + this._force3DLayerUpdate(); this._markersNeedUpdate = true; return; } let options: TerrainSpecification = terrainOptions; + const isUpdating = terrainOptions.source == null; if (drapeRenderMode === DrapeRenderMode.elevated) { // Input validation and source object unrolling if (typeof options.source === 'object') { @@ -2303,13 +2580,25 @@ class Style extends Evented { options = extend(options, {source: id}); } - if (this._validate(validateTerrain, 'terrain', options)) { + const validationOptions = extend({}, options); + const validationProps = {}; + + if (this.terrain && isUpdating) { + validationOptions.source = this.terrain.get().source; + + const fragmentStyle = this.terrain ? this.getFragmentStyle(this.terrain.scope) : null; + if (fragmentStyle) { + validationProps.style = fragmentStyle.serialize(); + } + } + + if (this._validate(validateTerrain, 'terrain', validationOptions, validationProps)) { return; } } // Enabling - if (!this.terrain || (this.terrain && drapeRenderMode !== this.terrain.drapeRenderMode)) { + if (!this.terrain || (this.terrain.scope !== this.scope && !isUpdating) || (this.terrain && drapeRenderMode !== this.terrain.drapeRenderMode)) { if (!options) return; this._createTerrain(options, drapeRenderMode); this.fire(new Event('data', {dataType: 'style'})); @@ -2326,7 +2615,7 @@ class Style extends Evented { } for (const key in terrainOptions) { if (!deepEqual(terrainOptions[key], currSpec[key])) { - terrain.set(terrainOptions); + terrain.set(terrainOptions, this.options); this.stylesheet.terrain = terrainOptions; const parameters = this._getTransitionParameters({duration: 0}); terrain.updateTransitions(parameters); @@ -2342,7 +2631,7 @@ class Style extends Evented { } _createFog(fogOptions: FogSpecification) { - const fog = this.fog = new Fog(fogOptions, this.map.transform); + const fog = this.fog = new Fog(fogOptions, this.map.transform, this.scope, this.options); this.stylesheet.fog = fog.get(); const parameters = this._getTransitionParameters({duration: 0}); fog.updateTransitions(parameters); @@ -2381,7 +2670,7 @@ class Style extends Evented { // Updating fog const fog = this.fog; if (!deepEqual(fog.get(), fogOptions)) { - fog.set(fogOptions); + fog.set(fogOptions, this.options); this.stylesheet.fog = fog.get(); const parameters = this._getTransitionParameters({duration: 0}); fog.updateTransitions(parameters); @@ -2420,10 +2709,14 @@ class Style extends Evented { } _createTerrain(terrainOptions: TerrainSpecification, drapeRenderMode: number) { - const terrain = this.terrain = new Terrain(terrainOptions, drapeRenderMode); - terrain.setScope(this.scope); + const terrain = this.terrain = new Terrain(terrainOptions, drapeRenderMode, this.scope, this.options); + + // We need to update the stylesheet only for the elevated mode, + // i.e., mock terrain shouldn't be propagated to the stylesheet + if (drapeRenderMode === DrapeRenderMode.elevated) { + this.stylesheet.terrain = terrainOptions; + } - this.stylesheet.terrain = terrainOptions; this.mergeTerrain(); this.updateDrapeFirstLayers(); this._force3DLayerUpdate(); @@ -2522,6 +2815,12 @@ class Style extends Evented { } } + reloadSources() { + for (const source of this.getSources()) { + if (source.reload) source.reload(); + } + } + updateSources(transform: Transform) { let lightDirection: ?Vec3; if (this.directionalLight) { @@ -2610,6 +2909,7 @@ class Style extends Evented { } if (placementCommitted || symbolBucketsChanged) { + this._buildingIndex.onNewFrame(transform.zoom); for (const layerId of this._mergedOrder) { const styleLayer = this._mergedLayers[layerId]; if (styleLayer.type !== 'symbol') continue; @@ -2630,18 +2930,92 @@ class Style extends Evented { // Fragments and merging - addImport(importSpec: ImportSpecification): Style { + addImport(importSpec: ImportSpecification, beforeId: ?string): Promise | void { this._checkLoaded(); const imports = this.stylesheet.imports = this.stylesheet.imports || []; const index = imports.findIndex(({id}) => id === importSpec.id); if (index !== -1) { - return this.fire(new ErrorEvent(new Error(`Import with id '${importSpec.id}' already exists in the map's style.`))); + this.fire(new ErrorEvent(new Error(`Import with id '${importSpec.id}' already exists in the map's style.`))); + return; + } + + if (!beforeId) { + imports.push(importSpec); + return this._loadImports([importSpec], true); + } + + const beforeIndex = imports.findIndex(({id}) => id === beforeId); + + if (beforeIndex === -1) { + this.fire(new ErrorEvent(new Error(`Import with id "${beforeId}" does not exist on this map.`))); } - imports.push(importSpec); - this._loadImports([importSpec], true); + this.stylesheet.imports = imports + .slice(0, beforeIndex) + .concat(importSpec) + .concat(imports.slice(beforeIndex)); + + return this._loadImports([importSpec], true, beforeId); + } + + updateImport(importId: string, importSpecification: ImportSpecification | string): Style { + this._checkLoaded(); + + const imports = this.stylesheet.imports || []; + const index = this.getImportIndex(importId); + if (index === -1) return this; + + if (typeof importSpecification === 'string') { + this.setImportUrl(importId, importSpecification); + return this; + } + + if (importSpecification.url && importSpecification.url !== imports[index].url) { + this.setImportUrl(importId, importSpecification.url); + } + + if (!deepEqual(importSpecification.config, imports[index].config)) { + this.setImportConfig(importId, importSpecification.config); + } + + if (!deepEqual(importSpecification.data, imports[index].data)) { + this.setImportData(importId, importSpecification.data); + } + + return this; + } + + moveImport(importId: string, beforeId: string): Style { + this._checkLoaded(); + + let imports = this.stylesheet.imports || []; + + const index = this.getImportIndex(importId); + if (index === -1) return this; + + const beforeIndex = this.getImportIndex(beforeId); + if (beforeIndex === -1) return this; + + const importSpec = imports[index]; + const fragment = this.fragments[index]; + + imports = imports.filter(({id}) => id !== importId); + + this.fragments = this.fragments.filter(({id}) => id !== importId); + + this.stylesheet.imports = imports + .slice(0, beforeIndex) + .concat(importSpec) + .concat(imports.slice(beforeIndex)); + + this.fragments = this.fragments + .slice(0, beforeIndex) + .concat(fragment) + .concat(this.fragments.slice(beforeIndex)); + + this.mergeLayers(); return this; } @@ -2656,13 +3030,11 @@ class Style extends Evented { // Update related fragment const fragment = this.fragments[index]; - fragment.style = this._createFragmentStyle({id: importId, url}); + fragment.style = this._createFragmentStyle(imports[index]); - const waitForStyle = new Promise(resolve => fragment.style.on('style.import.load', resolve)); + fragment.style.on('style.import.load', () => this.mergeAll()); fragment.style.loadURL(url); - waitForStyle.then(() => this._reloadImports()); - return this; } @@ -2706,17 +3078,18 @@ class Style extends Evented { fragment.config = config; fragment.style.setConfig(config, schema); - fragment.style.updateConfigDependencies(); + + this.updateConfigDependencies(); return this; } - removeImport(importId: string): Style { + removeImport(importId: string): void { this._checkLoaded(); const imports = this.stylesheet.imports || []; const index = this.getImportIndex(importId); - if (index === -1) return this; + if (index === -1) return; imports.splice(index, 1); @@ -2726,7 +3099,6 @@ class Style extends Evented { this.fragments.splice(index, 1); this._reloadImports(); - return this; } getImportIndex(importId: string): number { @@ -2785,7 +3157,17 @@ class Style extends Evented { this._mergedOtherSourceCaches[fqid]; } - getSourceCaches(fqid: string): Array { + /** + * Returns all source caches for a given style FQID. + * If no FQID is provided, returns all source caches, + * including source caches in imported styles. + * @param {string} fqid Style FQID. + * @returns {Array} List of source caches. + */ + getSourceCaches(fqid: ?string): Array { + // $FlowFixMe[incompatible-return] - Flow can't infer result of Object.values() + if (fqid == null) return Object.values(this._mergedSourceCaches); + const sourceCaches = []; if (this._mergedOtherSourceCaches[fqid]) { sourceCaches.push(this._mergedOtherSourceCaches[fqid]); @@ -2797,8 +3179,9 @@ class Style extends Evented { } updateSourceCaches() { - for (const fqid in this._changes.updatedSourceCaches) { - const action = this._changes.updatedSourceCaches[fqid]; + const updatedSourceCaches = this._changes.getUpdatedSourceCaches(); + for (const fqid in updatedSourceCaches) { + const action = updatedSourceCaches[fqid]; assert(action === 'reload' || action === 'clear'); if (action === 'reload') { this.reloadSource(fqid); @@ -2809,7 +3192,8 @@ class Style extends Evented { } updateLayers(parameters: EvaluationParameters) { - for (const id of this._changes.updatedPaintProps) { + const updatedPaintProps = this._changes.getUpdatedPaintProperties(); + for (const id of updatedPaintProps) { const layer = this.getLayer(id); if (layer) layer.updateTransitions(parameters); } @@ -2895,6 +3279,9 @@ class Style extends Evented { destroy() { this._clearWorkerCaches(); + this.fragments.forEach(fragment => { + fragment.style._remove(); + }); if (this.terrainSetForDrapingOnly()) { delete this.terrain; delete this.stylesheet.terrain; diff --git a/src/style/style_changes.js b/src/style/style_changes.js index 9ebd2af1250..71b183f369c 100644 --- a/src/style/style_changes.js +++ b/src/style/style_changes.js @@ -6,23 +6,56 @@ import type StyleLayer from './style_layer.js'; * Class for tracking style changes by scope, shared between all style instances. */ class StyleChanges { - changed: boolean; + _changed: boolean; _updatedLayers: {[_: string]: Set;}; _removedLayers: {[_: string]: {[_: string]: StyleLayer}}; - updatedPaintProps: Set; - changedImages: Set; - updatedSourceCaches: {[_: string]: 'clear' | 'reload'}; + _updatedPaintProps: Set; + _updatedImages: Set; + _updatedSourceCaches: {[_: string]: 'clear' | 'reload'}; constructor() { - this.changed = false; + this._changed = false; this._updatedLayers = {}; this._removedLayers = {}; - this.updatedSourceCaches = {}; - this.updatedPaintProps = new Set(); + this._updatedSourceCaches = {}; + this._updatedPaintProps = new Set(); - this.changedImages = new Set(); + this._updatedImages = new Set(); + } + + isDirty(): boolean { + return this._changed; + } + + /** + * Mark changes as dirty. + */ + setDirty() { + this._changed = true; + } + + getUpdatedSourceCaches(): {[_: string]: 'clear' | 'reload'} { + return this._updatedSourceCaches; + } + + /** + * Mark that a source cache needs to be cleared or reloaded. + * @param {string} id + * @param {'clear' | 'reload'} action + */ + updateSourceCache(id: string, action: 'clear' | 'reload') { + this._updatedSourceCaches[id] = action; + this.setDirty(); + } + + /** + * Discards updates to the source cache with the given id. + * @param {string} id + */ + discardSourceCacheUpdate(id: string) { + delete this._updatedSourceCaches[id]; } /** @@ -33,6 +66,7 @@ class StyleChanges { const scope = layer.scope; this._updatedLayers[scope] = this._updatedLayers[scope] || new Set(); this._updatedLayers[scope].add(layer.id); + this.setDirty(); } /** @@ -46,7 +80,9 @@ class StyleChanges { this._removedLayers[scope][layer.id] = layer; this._updatedLayers[scope].delete(layer.id); - this.updatedPaintProps.delete(layer.fqid); + this._updatedPaintProps.delete(layer.fqid); + + this.setDirty(); } /** @@ -87,16 +123,49 @@ class StyleChanges { return updatesByScope; } + getUpdatedPaintProperties(): Set { + return this._updatedPaintProps; + } + + /** + * Mark a layer as having a changed paint properties. + * @param {StyleLayer} layer + */ + updatePaintProperties(layer: StyleLayer) { + this._updatedPaintProps.add(layer.fqid); + this.setDirty(); + } + + getUpdatedImages(): Array { + return Array.from(this._updatedImages.values()); + } + + /** + * Mark an image as having changed. + * @param {string} id + */ + updateImage(id: string) { + this._updatedImages.add(id); + this.setDirty(); + } + + resetUpdatedImages() { + this._updatedImages.clear(); + } + + /** + * Reset all style changes. + */ reset() { - this.changed = false; + this._changed = false; this._updatedLayers = {}; this._removedLayers = {}; - this.updatedSourceCaches = {}; - this.updatedPaintProps.clear(); + this._updatedSourceCaches = {}; + this._updatedPaintProps.clear(); - this.changedImages.clear(); + this._updatedImages.clear(); } } diff --git a/src/style/style_image.js b/src/style/style_image.js index f06ebd6eede..c4e8f37b767 100644 --- a/src/style/style_image.js +++ b/src/style/style_image.js @@ -2,7 +2,7 @@ import {RGBAImage} from '../util/image.js'; -import type Map from '../ui/map.js'; +import type {Map} from '../ui/map.js'; export type StyleImageData = { data: RGBAImage, diff --git a/src/style/style_layer.js b/src/style/style_layer.js index e5292574e25..f2b6bacc4ff 100644 --- a/src/style/style_layer.js +++ b/src/style/style_layer.js @@ -2,13 +2,6 @@ import {endsWith, filterObject} from '../util/util.js'; -import styleSpec from '../style-spec/reference/latest.js'; -import { - validateStyle, - validateLayoutProperty, - validatePaintProperty, - emitValidationErrors -} from './validate_style.js'; import {Evented} from '../util/evented.js'; import {Layout, Transitionable, Transitioning, Properties, PossiblyEvaluated, PossiblyEvaluatedPropertyValue} from './properties.js'; import {supportsPropertyExpression} from '../style-spec/util/properties.js'; @@ -16,11 +9,10 @@ import featureFilter from '../style-spec/feature_filter/index.js'; import {makeFQID} from '../util/fqid.js'; import type {FeatureState} from '../style-spec/expression/index.js'; -import type {Expression} from '../style-spec/expression/expression.js'; import type {Bucket} from '../data/bucket.js'; import type Point from '@mapbox/point-geometry'; import type {FeatureFilter, FilterExpression} from '../style-spec/feature_filter/index.js'; -import type {TransitionParameters, PropertyValue} from './properties.js'; +import type {TransitionParameters, PropertyValue, ConfigOptions} from './properties.js'; import type EvaluationParameters from './evaluation_parameters.js'; import type Transform from '../geo/transform.js'; import type { @@ -30,16 +22,25 @@ import type { PropertyValueSpecification } from '../style-spec/types.js'; import type {CustomLayerInterface} from './style_layer/custom_style_layer.js'; -import type MapboxMap from '../ui/map.js'; -import type {StyleSetterOptions} from './style.js'; +import type {Map as MapboxMap} from '../ui/map.js'; import type {TilespaceQueryGeometry} from './query_geometry.js'; import type {DEMSampler} from '../terrain/elevation.js'; import type {IVectorTileFeature} from '@mapbox/vector-tile'; import type {CreateProgramParams} from "../render/painter.js"; import type SourceCache from '../source/source_cache.js'; +import type Painter from '../render/painter.js'; +import type {QueryFeature} from '../util/vectortile_to_geojson.js'; const TRANSITION_SUFFIX = '-transition'; +type LayerRenderingStats = { + numRenderedVerticesInTransparentPass: number; + numRenderedVerticesInShadowPass: number; +}; + +// Symbols are draped only on native and for certain cases only +const drapedLayers = new Set(['fill', 'line', 'background', 'hillshade', 'raster']); + class StyleLayer extends Evented { id: string; fqid: string; @@ -65,28 +66,18 @@ class StyleLayer extends Evented { _featureFilter: FeatureFilter; _filterCompiled: boolean; - options: ?Map; - - +queryRadius: (bucket: Bucket) => number; - +queryIntersectsFeature: (queryGeometry: TilespaceQueryGeometry, - feature: IVectorTileFeature, - featureState: FeatureState, - geometry: Array>, - zoom: number, - transform: Transform, - pixelPosMatrix: Float32Array, - elevationHelper: ?DEMSampler, - layoutVertexArrayOffset: number) => boolean | number; + options: ?ConfigOptions; + _stats: ?LayerRenderingStats; - +onAdd: ?(map: MapboxMap) => void; - +onRemove: ?(map: MapboxMap) => void; - +isLayerDraped: ?(sourceCache: ?SourceCache) => boolean; - - constructor(layer: LayerSpecification | CustomLayerInterface, properties: $ReadOnly<{layout?: Properties<*>, paint?: Properties<*>}>, options?: ?Map) { + constructor(layer: LayerSpecification | CustomLayerInterface, properties: $ReadOnly<{layout?: Properties<*>, paint?: Properties<*>}>, scope: string, options?: ?ConfigOptions) { super(); this.id = layer.id; + this.fqid = makeFQID(this.id, scope); this.type = layer.type; + this.scope = scope; + this.options = options; + this._featureFilter = {filter: () => true, needGeometry: false, needFeature: false}; this._filterCompiled = false; this.isConfigDependent = false; @@ -105,23 +96,21 @@ class StyleLayer extends Evented { this.filter = layer.filter; } - this.options = options; - if (layer.slot) this.slot = layer.slot; if (properties.layout) { - this._unevaluatedLayout = new Layout(properties.layout, options); + this._unevaluatedLayout = new Layout(properties.layout, this.scope, options); this.isConfigDependent = this.isConfigDependent || this._unevaluatedLayout.isConfigDependent; } if (properties.paint) { - this._transitionablePaint = new Transitionable(properties.paint, options); + this._transitionablePaint = new Transitionable(properties.paint, this.scope, options); for (const property in layer.paint) { - this.setPaintProperty(property, layer.paint[property], {validate: false}); + this.setPaintProperty(property, layer.paint[property]); } for (const property in layer.layout) { - this.setLayoutProperty(property, layer.layout[property], {validate: false}); + this.setLayoutProperty(property, layer.layout[property]); } this.isConfigDependent = this.isConfigDependent || this._transitionablePaint.isConfigDependent; @@ -131,14 +120,14 @@ class StyleLayer extends Evented { } } - /** - * Sets the scope of the style layer to a particular Style. - * - * @private - */ - setScope(scope: string) { - this.scope = scope; - this.fqid = makeFQID(this.id, scope); + // No-op in the StyleLayer class, must be implemented by each concrete StyleLayer + onAdd(_map: MapboxMap): void {} + + // No-op in the StyleLayer class, must be implemented by each concrete StyleLayer + onRemove(_map: MapboxMap): void {} + + isDraped(_sourceCache?: SourceCache | void): boolean { + return drapedLayers.has(this.type); } getLayoutProperty(name: string): PropertyValueSpecification { @@ -149,14 +138,7 @@ class StyleLayer extends Evented { return this._unevaluatedLayout.getValue(name); } - setLayoutProperty(name: string, value: any, options: StyleSetterOptions = {}) { - if (value !== null && value !== undefined) { - const key = `layers.${this.id}.layout.${name}`; - if (this._validate(validateLayoutProperty, key, name, value, options)) { - return; - } - } - + setLayoutProperty(name: string, value: any) { if (this.type === 'custom' && name === 'visibility') { this.visibility = value; return; @@ -186,14 +168,7 @@ class StyleLayer extends Evented { } } - setPaintProperty(name: string, value: mixed, options: StyleSetterOptions = {}): boolean { - if (value !== null && value !== undefined) { - const key = `layers.${this.id}.paint.${name}`; - if (this._validate(validatePaintProperty, key, name, value, options)) { - return false; - } - } - + setPaintProperty(name: string, value: mixed): boolean { const paint = this._transitionablePaint; const specProps = paint._properties.properties; @@ -291,21 +266,6 @@ class StyleLayer extends Evented { }); } - _validate(validate: Function, key: string, name: string, value: mixed, options: StyleSetterOptions = {}): boolean { - if (options && options.validate === false) { - return false; - } - return emitValidationErrors(this, validate.call(validateStyle, { - key, - layerType: this.type, - objectKey: name, - value, - styleSpec, - // Workaround for https://github.com/mapbox/mapbox-gl-js/issues/2407 - style: {glyphs: true, sprite: true} - })); - } - is3D(): boolean { return false; } @@ -338,6 +298,10 @@ class StyleLayer extends Evented { return 0.0; } + tileCoverLift(): number { + return 0.0; + } + resize() { // noop } @@ -375,6 +339,44 @@ class StyleLayer extends Evented { dynamicFilterNeedsFeature(): boolean { return this._featureFilter.needFeature; } + + getLayerRenderingStats(): ?LayerRenderingStats { + return this._stats; + } + + resetLayerRenderingStats(painter: Painter) { + if (this._stats) { + if (painter.renderPass === 'shadow') { + this._stats.numRenderedVerticesInShadowPass = 0; + } else { + this._stats.numRenderedVerticesInTransparentPass = 0; + } + } + } + + // $FlowFixMe[incompatible-return] - No-op in the StyleLayer class, must be implemented by each concrete StyleLayer + queryRadius(_bucket: Bucket): number {} + + queryIntersectsFeature( + _queryGeometry: TilespaceQueryGeometry, + _feature: IVectorTileFeature, + _featureState: FeatureState, + _geometry: Array>, + _zoom: number, + _transform: Transform, + _pixelPosMatrix: Float32Array, + _elevationHelper: ?DEMSampler, + _layoutVertexArrayOffset: number + // $FlowFixMe[incompatible-return] - No-op in the StyleLayer class, must be implemented by each concrete StyleLayer + ): boolean | number {} + + queryIntersectsMatchingFeature( + _queryGeometry: TilespaceQueryGeometry, + _featureIndex: number, + _filter: FeatureFilter, + _transform: Transform + // $FlowFixMe[incompatible-return] - No-op in the StyleLayer class, must be implemented by each concrete StyleLayer + ): {queryFeature: ?QueryFeature, intersectionZ: number} {} } export default StyleLayer; diff --git a/src/style/style_layer/background_style_layer.js b/src/style/style_layer/background_style_layer.js index ff7a161caff..db3df708cd9 100644 --- a/src/style/style_layer/background_style_layer.js +++ b/src/style/style_layer/background_style_layer.js @@ -7,16 +7,16 @@ import {Transitionable, Transitioning, PossiblyEvaluated} from '../properties.js import type {PaintProps} from './background_style_layer_properties.js'; import type {LayerSpecification} from '../../style-spec/types.js'; -import type {Expression} from '../../style-spec/expression/expression.js'; import type {CreateProgramParams} from "../../render/painter.js"; +import type {ConfigOptions} from '../properties.js'; class BackgroundStyleLayer extends StyleLayer { _transitionablePaint: Transitionable; _transitioningPaint: Transitioning; paint: PossiblyEvaluated; - constructor(layer: LayerSpecification, options?: ?Map) { - super(layer, properties, options); + constructor(layer: LayerSpecification, scope: string, options?: ?ConfigOptions) { + super(layer, properties, scope, options); } getProgramIds(): Array { diff --git a/src/style/style_layer/background_style_layer_properties.js b/src/style/style_layer/background_style_layer_properties.js index cb6dc42d3dc..718065dee35 100644 --- a/src/style/style_layer/background_style_layer_properties.js +++ b/src/style/style_layer/background_style_layer_properties.js @@ -1,4 +1,4 @@ -// This file is generated. Edit build/generate-style-code.js, then run `yarn run codegen`. +// This file is generated. Edit build/generate-style-code.js, then run `npm run codegen`. // @flow /* eslint-disable */ diff --git a/src/style/style_layer/circle_style_layer.js b/src/style/style_layer/circle_style_layer.js index 6a579297f53..4c7901649c8 100644 --- a/src/style/style_layer/circle_style_layer.js +++ b/src/style/style_layer/circle_style_layer.js @@ -16,7 +16,6 @@ import {latFromMercatorY, mercatorZfromAltitude} from '../../geo/mercator_coordi import EXTENT from '../../style-spec/data/extent.js'; import type {FeatureState} from '../../style-spec/expression/index.js'; -import type {Expression} from '../../style-spec/expression/expression.js'; import type Transform from '../../geo/transform.js'; import type {Bucket, BucketParameters} from '../../data/bucket.js'; import type {LayoutProps, PaintProps} from './circle_style_layer_properties.js'; @@ -27,6 +26,7 @@ import type {IVectorTileFeature} from '@mapbox/vector-tile'; import {circleDefinesValues} from '../../render/program/circle_program.js'; import type {CreateProgramParams} from "../../render/painter.js"; import type {DynamicDefinesType} from "../../render/program/program_uniforms.js"; +import type {ConfigOptions} from '../properties.js'; class CircleStyleLayer extends StyleLayer { _unevaluatedLayout: Layout; @@ -36,8 +36,8 @@ class CircleStyleLayer extends StyleLayer { _transitioningPaint: Transitioning; paint: PossiblyEvaluated; - constructor(layer: LayerSpecification, options?: ?Map) { - super(layer, properties, options); + constructor(layer: LayerSpecification, scope: string, options?: ?ConfigOptions) { + super(layer, properties, scope, options); } createBucket(parameters: BucketParameters): CircleBucket { diff --git a/src/style/style_layer/circle_style_layer_properties.js b/src/style/style_layer/circle_style_layer_properties.js index f79eb0bbb7f..0afb925ec94 100644 --- a/src/style/style_layer/circle_style_layer_properties.js +++ b/src/style/style_layer/circle_style_layer_properties.js @@ -1,4 +1,4 @@ -// This file is generated. Edit build/generate-style-code.js, then run `yarn run codegen`. +// This file is generated. Edit build/generate-style-code.js, then run `npm run codegen`. // @flow /* eslint-disable */ diff --git a/src/style/style_layer/custom_style_layer.js b/src/style/style_layer/custom_style_layer.js index df53b103eb7..b415b619bc6 100644 --- a/src/style/style_layer/custom_style_layer.js +++ b/src/style/style_layer/custom_style_layer.js @@ -2,11 +2,12 @@ import StyleLayer from '../style_layer.js'; import MercatorCoordinate from '../../geo/mercator_coordinate.js'; -import type Map from '../../ui/map.js'; +import type {Map} from '../../ui/map.js'; import assert from 'assert'; + import type {ValidationErrors} from '../validate_style.js'; import type {ProjectionSpecification} from '../../style-spec/types.js'; -import SourceCache from '../../source/source_cache.js'; +import type SourceCache from '../../source/source_cache.js'; type CustomRenderMethod = (gl: WebGL2RenderingContext, matrix: Array, projection: ?ProjectionSpecification, projectionToMercatorMatrix: ?Array, projectionToMercatorTransition: ?number, centerInMercator: ?Array, pixelsPerMeterRatio: ?number) => void; @@ -195,8 +196,8 @@ class CustomStyleLayer extends StyleLayer { implementation: CustomLayerInterface; - constructor(implementation: CustomLayerInterface) { - super(implementation, {}); + constructor(implementation: CustomLayerInterface, scope: string) { + super(implementation, {}, scope); this.implementation = implementation; if (implementation.slot) this.slot = implementation.slot; } @@ -209,8 +210,7 @@ class CustomStyleLayer extends StyleLayer { return this.implementation.prerender !== undefined; } - // $FlowFixMe[method-unbinding] - isLayerDraped(_: ?SourceCache): boolean { + isDraped(_: ?SourceCache): boolean { return this.implementation.renderToTile !== undefined; } @@ -224,8 +224,7 @@ class CustomStyleLayer extends StyleLayer { return false; } - // $FlowFixMe[incompatible-extend] - CustomStyleLayer is not serializable - serialize() { + serialize(): any { assert(false, "Custom layers cannot be serialized"); } diff --git a/src/style/style_layer/fill_extrusion_style_layer.js b/src/style/style_layer/fill_extrusion_style_layer.js index af564ee8023..ab756395a87 100644 --- a/src/style/style_layer/fill_extrusion_style_layer.js +++ b/src/style/style_layer/fill_extrusion_style_layer.js @@ -12,7 +12,6 @@ import EXTENT from '../../style-spec/data/extent.js'; import {CanonicalTileID} from '../../source/tile_id.js'; import type {FeatureState} from '../../style-spec/expression/index.js'; -import type {Expression} from '../../style-spec/expression/expression.js'; import type {BucketParameters} from '../../data/bucket.js'; import type {PaintProps, LayoutProps} from './fill_extrusion_style_layer_properties.js'; import type Transform from '../../geo/transform.js'; @@ -21,6 +20,7 @@ import type {TilespaceQueryGeometry} from '../query_geometry.js'; import type {DEMSampler} from '../../terrain/elevation.js'; import type {Vec2, Vec4} from 'gl-matrix'; import type {IVectorTileFeature} from '@mapbox/vector-tile'; +import type {ConfigOptions} from '../properties.js'; class Point3D extends Point { z: number; @@ -37,8 +37,9 @@ class FillExtrusionStyleLayer extends StyleLayer { paint: PossiblyEvaluated; layout: PossiblyEvaluated; - constructor(layer: LayerSpecification, options?: ?Map) { - super(layer, properties, options); + constructor(layer: LayerSpecification, scope: string, options?: ?ConfigOptions) { + super(layer, properties, scope, options); + this._stats = {numRenderedVerticesInShadowPass : 0, numRenderedVerticesInTransparentPass: 0}; } createBucket(parameters: BucketParameters): FillExtrusionBucket { diff --git a/src/style/style_layer/fill_extrusion_style_layer_properties.js b/src/style/style_layer/fill_extrusion_style_layer_properties.js index 4f32ac53ffb..75ba87fdc0f 100644 --- a/src/style/style_layer/fill_extrusion_style_layer_properties.js +++ b/src/style/style_layer/fill_extrusion_style_layer_properties.js @@ -1,4 +1,4 @@ -// This file is generated. Edit build/generate-style-code.js, then run `yarn run codegen`. +// This file is generated. Edit build/generate-style-code.js, then run `npm run codegen`. // @flow /* eslint-disable */ @@ -49,6 +49,7 @@ export type PaintProps = {| "fill-extrusion-vertical-scale": DataConstantProperty, "fill-extrusion-rounded-roof": DataConstantProperty, "fill-extrusion-cutoff-fade-range": DataConstantProperty, + "fill-extrusion-emissive-strength": DataConstantProperty, |}; const paint: Properties = new Properties({ @@ -73,6 +74,7 @@ const paint: Properties = new Properties({ "fill-extrusion-vertical-scale": new DataConstantProperty(styleSpec["paint_fill-extrusion"]["fill-extrusion-vertical-scale"]), "fill-extrusion-rounded-roof": new DataConstantProperty(styleSpec["paint_fill-extrusion"]["fill-extrusion-rounded-roof"]), "fill-extrusion-cutoff-fade-range": new DataConstantProperty(styleSpec["paint_fill-extrusion"]["fill-extrusion-cutoff-fade-range"]), + "fill-extrusion-emissive-strength": new DataConstantProperty(styleSpec["paint_fill-extrusion"]["fill-extrusion-emissive-strength"]), }); // Note: without adding the explicit type annotation, Flow infers weaker types diff --git a/src/style/style_layer/fill_style_layer.js b/src/style/style_layer/fill_style_layer.js index c7a00b64ed9..54eceae7b73 100644 --- a/src/style/style_layer/fill_style_layer.js +++ b/src/style/style_layer/fill_style_layer.js @@ -10,7 +10,6 @@ import {Transitionable, Transitioning, Layout, PossiblyEvaluated} from '../prope import ProgramConfiguration from '../../data/program_configuration.js'; import type {FeatureState} from '../../style-spec/expression/index.js'; -import type {Expression} from '../../style-spec/expression/expression.js'; import type {BucketParameters} from '../../data/bucket.js'; import type Point from '@mapbox/point-geometry'; import type {LayoutProps, PaintProps} from './fill_style_layer_properties.js'; @@ -20,6 +19,7 @@ import type {LayerSpecification} from '../../style-spec/types.js'; import type {TilespaceQueryGeometry} from '../query_geometry.js'; import type {IVectorTileFeature} from '@mapbox/vector-tile'; import type {CreateProgramParams} from "../../render/painter.js"; +import type {ConfigOptions} from '../properties.js'; class FillStyleLayer extends StyleLayer { _unevaluatedLayout: Layout; @@ -29,8 +29,8 @@ class FillStyleLayer extends StyleLayer { _transitioningPaint: Transitioning; paint: PossiblyEvaluated; - constructor(layer: LayerSpecification, options?: ?Map) { - super(layer, properties, options); + constructor(layer: LayerSpecification, scope: string, options?: ?ConfigOptions) { + super(layer, properties, scope, options); } getProgramIds(): string[] { diff --git a/src/style/style_layer/fill_style_layer_properties.js b/src/style/style_layer/fill_style_layer_properties.js index 0e7c849f878..49dc82dc8a8 100644 --- a/src/style/style_layer/fill_style_layer_properties.js +++ b/src/style/style_layer/fill_style_layer_properties.js @@ -1,4 +1,4 @@ -// This file is generated. Edit build/generate-style-code.js, then run `yarn run codegen`. +// This file is generated. Edit build/generate-style-code.js, then run `npm run codegen`. // @flow /* eslint-disable */ diff --git a/src/style/style_layer/heatmap_style_layer.js b/src/style/style_layer/heatmap_style_layer.js index 0712cd872df..b3c85fbe343 100644 --- a/src/style/style_layer/heatmap_style_layer.js +++ b/src/style/style_layer/heatmap_style_layer.js @@ -20,11 +20,11 @@ import ProgramConfiguration from '../../data/program_configuration.js'; import type {TilespaceQueryGeometry} from '../query_geometry.js'; import type {DEMSampler} from '../../terrain/elevation.js'; import type {FeatureState} from '../../style-spec/expression/index.js'; -import type {Expression} from '../../style-spec/expression/expression.js'; import type Transform from '../../geo/transform.js'; import type CircleBucket from '../../data/bucket/circle_bucket.js'; import type {IVectorTileFeature} from '@mapbox/vector-tile'; import type {CreateProgramParams} from "../../render/painter.js"; +import type {ConfigOptions} from '../properties.js'; class HeatmapStyleLayer extends StyleLayer { @@ -40,8 +40,8 @@ class HeatmapStyleLayer extends StyleLayer { return new HeatmapBucket(parameters); } - constructor(layer: LayerSpecification, options?: ?Map) { - super(layer, properties, options); + constructor(layer: LayerSpecification, scope: string, options?: ?ConfigOptions) { + super(layer, properties, scope, options); // make sure color ramp texture is generated for default heatmap color too this._updateColorRamp(); diff --git a/src/style/style_layer/heatmap_style_layer_properties.js b/src/style/style_layer/heatmap_style_layer_properties.js index b243b87727c..56042c50758 100644 --- a/src/style/style_layer/heatmap_style_layer_properties.js +++ b/src/style/style_layer/heatmap_style_layer_properties.js @@ -1,4 +1,4 @@ -// This file is generated. Edit build/generate-style-code.js, then run `yarn run codegen`. +// This file is generated. Edit build/generate-style-code.js, then run `npm run codegen`. // @flow /* eslint-disable */ diff --git a/src/style/style_layer/hillshade_style_layer.js b/src/style/style_layer/hillshade_style_layer.js index 5dbdc62a5b0..5dbe96706f5 100644 --- a/src/style/style_layer/hillshade_style_layer.js +++ b/src/style/style_layer/hillshade_style_layer.js @@ -7,16 +7,16 @@ import {Transitionable, Transitioning, PossiblyEvaluated} from '../properties.js import type {PaintProps} from './hillshade_style_layer_properties.js'; import type {LayerSpecification} from '../../style-spec/types.js'; -import type {Expression} from '../../style-spec/expression/expression.js'; import type {CreateProgramParams} from "../../render/painter.js"; +import type {ConfigOptions} from '../properties.js'; class HillshadeStyleLayer extends StyleLayer { _transitionablePaint: Transitionable; _transitioningPaint: Transitioning; paint: PossiblyEvaluated; - constructor(layer: LayerSpecification, options?: ?Map) { - super(layer, properties, options); + constructor(layer: LayerSpecification, scope: string, options?: ?ConfigOptions) { + super(layer, properties, scope, options); } hasOffscreenPass(): boolean { diff --git a/src/style/style_layer/hillshade_style_layer_properties.js b/src/style/style_layer/hillshade_style_layer_properties.js index 603f5c23a10..64a44a0e703 100644 --- a/src/style/style_layer/hillshade_style_layer_properties.js +++ b/src/style/style_layer/hillshade_style_layer_properties.js @@ -1,4 +1,4 @@ -// This file is generated. Edit build/generate-style-code.js, then run `yarn run codegen`. +// This file is generated. Edit build/generate-style-code.js, then run `npm run codegen`. // @flow /* eslint-disable */ diff --git a/src/style/style_layer/layer_properties.js.ejs b/src/style/style_layer/layer_properties.js.ejs index 7b2be66b65a..80d13d63c09 100644 --- a/src/style/style_layer/layer_properties.js.ejs +++ b/src/style/style_layer/layer_properties.js.ejs @@ -5,7 +5,7 @@ const srcDir = locals.srcDir; const styleDir = locals.styleDir; -%> -// This file is generated. Edit build/generate-style-code.js, then run `yarn run codegen`. +// This file is generated. Edit build/generate-style-code.js, then run `npm run codegen`. // @flow /* eslint-disable */ @@ -23,7 +23,7 @@ import type Color from '<%= srcDir -%>/style-spec/util/color.js'; import type Formatted from '<%= srcDir %>/style-spec/expression/types/formatted.js'; import type ResolvedImage from '<%= srcDir -%>/style-spec/expression/types/resolved_image.js'; -<% +<% const overridables = paintProperties.filter(p => p.overridable) if (overridables.length) { -%> diff --git a/src/style/style_layer/line_style_layer.js b/src/style/style_layer/line_style_layer.js index 05a9686e94e..2fc2421b287 100644 --- a/src/style/style_layer/line_style_layer.js +++ b/src/style/style_layer/line_style_layer.js @@ -13,9 +13,8 @@ import {Transitionable, Transitioning, Layout, PossiblyEvaluated, DataDrivenProp import ProgramConfiguration from '../../data/program_configuration.js'; import Step from '../../style-spec/expression/definitions/step.js'; -import type {PossiblyEvaluatedValue, PropertyValue, PossiblyEvaluatedPropertyValue} from '../properties.js'; +import type {PossiblyEvaluatedValue, PropertyValue, PossiblyEvaluatedPropertyValue, ConfigOptions} from '../properties.js'; import type {Feature, FeatureState, ZoomConstantExpression, StylePropertyExpression} from '../../style-spec/expression/index.js'; -import type {Expression} from '../../style-spec/expression/expression.js'; import type {Bucket, BucketParameters} from '../../data/bucket.js'; import type {LayoutProps, PaintProps} from './line_style_layer_properties.js'; import type Transform from '../../geo/transform.js'; @@ -58,8 +57,8 @@ class LineStyleLayer extends StyleLayer { _transitioningPaint: Transitioning; paint: PossiblyEvaluated; - constructor(layer: LayerSpecification, options?: ?Map) { - super(layer, properties, options); + constructor(layer: LayerSpecification, scope: string, options?: ?ConfigOptions) { + super(layer, properties, scope, options); this.gradientVersion = 0; } diff --git a/src/style/style_layer/line_style_layer_properties.js b/src/style/style_layer/line_style_layer_properties.js index be4a6f3312f..afbe8e9d08a 100644 --- a/src/style/style_layer/line_style_layer_properties.js +++ b/src/style/style_layer/line_style_layer_properties.js @@ -1,4 +1,4 @@ -// This file is generated. Edit build/generate-style-code.js, then run `yarn run codegen`. +// This file is generated. Edit build/generate-style-code.js, then run `npm run codegen`. // @flow /* eslint-disable */ @@ -19,7 +19,7 @@ import type ResolvedImage from '../../style-spec/expression/types/resolved_image export type LayoutProps = {| "line-cap": DataDrivenProperty<"butt" | "round" | "square">, - "line-join": DataDrivenProperty<"bevel" | "round" | "miter">, + "line-join": DataDrivenProperty<"bevel" | "round" | "miter" | "none">, "line-miter-limit": DataConstantProperty, "line-round-limit": DataConstantProperty, "line-sort-key": DataDrivenProperty, diff --git a/src/style/style_layer/raster_particle_style_layer.js b/src/style/style_layer/raster_particle_style_layer.js new file mode 100644 index 00000000000..8e19d5b7e92 --- /dev/null +++ b/src/style/style_layer/raster_particle_style_layer.js @@ -0,0 +1,103 @@ +// @flow + +import StyleLayer from '../style_layer.js'; +import browser from '../../util/browser.js'; +import properties from './raster_particle_style_layer_properties.js'; +import {PossiblyEvaluated} from '../properties.js'; +import {renderColorRamp} from '../../util/color_ramp.js'; +import {RGBAImage} from '../../util/image.js'; + +import type {ConfigOptions} from "../properties"; +import type {Map as MapboxMap} from '../../ui/map.js'; +import type {PaintProps} from './raster_particle_style_layer_properties.js'; +import type {LayerSpecification} from '../../style-spec/types.js'; +import type Texture from '../../render/texture.js'; +import type Framebuffer from '../../gl/framebuffer.js'; +import type SourceCache from '../../source/source_cache.js'; + +const COLOR_RAMP_RES = 256; + +class RasterParticleStyleLayer extends StyleLayer { + paint: PossiblyEvaluated; + + // Shared rendering resources + + colorRamp: RGBAImage; + colorRampTexture: ?Texture; + tileFramebuffer: Framebuffer; + particleFramebuffer: Framebuffer; + + previousDrawTimestamp: ?number; + lastInvalidatedAt: number; + + constructor(layer: LayerSpecification, scope: string, options?: ?ConfigOptions) { + super(layer, properties, scope, options); + this._updateColorRamp(); + this.lastInvalidatedAt = browser.now(); + } + + onRemove(_: MapboxMap): void { + if (this.colorRampTexture) { + this.colorRampTexture.destroy(); + } + + if (this.tileFramebuffer) { + this.tileFramebuffer.destroy(); + } + + if (this.particleFramebuffer) { + this.particleFramebuffer.destroy(); + } + } + + hasColorMap(): boolean { + const expr = this._transitionablePaint._values['raster-particle-color'].value; + return !!expr.value; + } + + getProgramIds(): Array { + return ['rasterParticle']; + } + + hasOffscreenPass(): boolean { + return this.visibility !== 'none'; + } + + isDraped(_: ?SourceCache): boolean { + return false; + } + + _handleSpecialPaintPropertyUpdate(name: string) { + if (name === 'raster-particle-color' || name === 'raster-particle-max-speed') { + this._updateColorRamp(); + this._invalidateAnimationState(); + } + + if (name === 'raster-particle-count') { + this._invalidateAnimationState(); + } + } + + _updateColorRamp() { + if (!this.hasColorMap()) return; + + const expression = this._transitionablePaint._values['raster-particle-color'].value.expression; + const end = this._transitionablePaint._values['raster-particle-max-speed'].value.expression.evaluate({zoom: 0}); + + this.colorRamp = renderColorRamp({ + expression, + evaluationKey: 'rasterParticleSpeed', + image: this.colorRamp, + clips: [{start:0, end}], + resolution: COLOR_RAMP_RES, + }); + this.colorRampTexture = null; + } + + _invalidateAnimationState() { + this.lastInvalidatedAt = browser.now(); + } +} + +export {COLOR_RAMP_RES}; +export default RasterParticleStyleLayer; diff --git a/src/style/style_layer/raster_particle_style_layer_properties.js b/src/style/style_layer/raster_particle_style_layer_properties.js new file mode 100644 index 00000000000..d8f14d4241b --- /dev/null +++ b/src/style/style_layer/raster_particle_style_layer_properties.js @@ -0,0 +1,53 @@ +// This file is generated. Edit build/generate-style-code.js, then run `npm run codegen`. +// @flow +/* eslint-disable */ + +import styleSpec from '../../style-spec/reference/latest.js'; + +import { + Properties, + DataConstantProperty, + DataDrivenProperty, + ColorRampProperty +} from '../properties.js'; + +import type Color from '../../style-spec/util/color.js'; + +import type Formatted from '../../style-spec/expression/types/formatted.js'; + +import type ResolvedImage from '../../style-spec/expression/types/resolved_image.js'; + +export type LayoutProps = {| + "visibility": DataConstantProperty<"visible" | "none">, +|}; + +const layout: Properties = new Properties({ + "visibility": new DataConstantProperty(styleSpec["layout_raster-particle"]["visibility"]), +}); + +export type PaintProps = {| + "raster-particle-array-band": DataConstantProperty, + "raster-particle-count": DataConstantProperty, + "raster-particle-color": ColorRampProperty, + "raster-particle-max-speed": DataConstantProperty, + "raster-particle-speed-factor": DataConstantProperty, + "raster-particle-fade-opacity-factor": DataConstantProperty, + "raster-particle-reset-rate-factor": DataConstantProperty, +|}; + +const paint: Properties = new Properties({ + "raster-particle-array-band": new DataConstantProperty(styleSpec["paint_raster-particle"]["raster-particle-array-band"]), + "raster-particle-count": new DataConstantProperty(styleSpec["paint_raster-particle"]["raster-particle-count"]), + "raster-particle-color": new ColorRampProperty(styleSpec["paint_raster-particle"]["raster-particle-color"]), + "raster-particle-max-speed": new DataConstantProperty(styleSpec["paint_raster-particle"]["raster-particle-max-speed"]), + "raster-particle-speed-factor": new DataConstantProperty(styleSpec["paint_raster-particle"]["raster-particle-speed-factor"]), + "raster-particle-fade-opacity-factor": new DataConstantProperty(styleSpec["paint_raster-particle"]["raster-particle-fade-opacity-factor"]), + "raster-particle-reset-rate-factor": new DataConstantProperty(styleSpec["paint_raster-particle"]["raster-particle-reset-rate-factor"]), +}); + +// Note: without adding the explicit type annotation, Flow infers weaker types +// for these objects from their use in the constructor to StyleLayer, as +// {layout?: Properties<...>, paint: Properties<...>} +export default ({ paint, layout }: $Exact<{ + paint: Properties, layout: Properties +}>); diff --git a/src/style/style_layer/raster_style_layer.js b/src/style/style_layer/raster_style_layer.js index 4b04fdd4455..81452a6d561 100644 --- a/src/style/style_layer/raster_style_layer.js +++ b/src/style/style_layer/raster_style_layer.js @@ -6,15 +6,16 @@ import properties from './raster_style_layer_properties.js'; import {Transitionable, Transitioning, PossiblyEvaluated} from '../properties.js'; import {renderColorRamp} from '../../util/color_ramp.js'; import {RGBAImage} from '../../util/image.js'; +import ImageSource from '../../source/image_source.js'; import type {PaintProps} from './raster_style_layer_properties.js'; import type {LayerSpecification} from '../../style-spec/types.js'; import type Texture from '../../render/texture.js'; -import type {Expression} from '../../style-spec/expression/expression.js'; -import ImageSource from '../../source/image_source.js'; -import SourceCache from '../../source/source_cache.js'; +import type {ConfigOptions} from '../properties.js'; +import type SourceCache from '../../source/source_cache.js'; -const COLOR_RAMP_RES = 256; +export const COLOR_RAMP_RES = 256; +export const COLOR_MIX_FACTOR = (Math.pow(COLOR_RAMP_RES, 2) - 1) / (255 * COLOR_RAMP_RES * (COLOR_RAMP_RES + 3)); class RasterStyleLayer extends StyleLayer { _transitionablePaint: Transitionable; @@ -24,9 +25,17 @@ class RasterStyleLayer extends StyleLayer { colorRamp: RGBAImage; colorRampTexture: ?Texture; - constructor(layer: LayerSpecification, options?: ?Map) { - super(layer, properties, options); - this._updateColorRamp(); + // Cache the currently-computed range so that we can call updateColorRamp + // during raster color rendering, at which point we can make use of the + // source's data range in case raster-color-range is not explicitly specified + // in the style. This allows us to call multiple times and only update if + // it's changed. + _curRampRange: [number, number]; + + constructor(layer: LayerSpecification, scope: string, options?: ?ConfigOptions) { + super(layer, properties, scope, options); + this.updateColorRamp(); + this._curRampRange = [NaN, NaN]; } getProgramIds(): Array { @@ -38,27 +47,39 @@ class RasterStyleLayer extends StyleLayer { return !!expr.value; } - // $FlowFixMe[method-unbinding] - isLayerDraped(sourceCache: ?SourceCache): boolean { + tileCoverLift(): number { + return this.paint.get('raster-elevation'); + } + + isDraped(sourceCache: ?SourceCache): boolean { // Special handling for raster, where the drapeability depends on the source - // If tile ID is missing, it's rendered outside of the tile pyramid (eg. poles) - if (sourceCache && sourceCache._source instanceof ImageSource && (sourceCache._source.onNorthPole || sourceCache._source.onSouthPole)) { - return false; + if (sourceCache && sourceCache._source instanceof ImageSource) { + // If tile ID is missing, it's rendered outside of the tile pyramid (eg. poles) + if (sourceCache._source.onNorthPole || sourceCache._source.onSouthPole) { + return false; + } } - return true; + return this.paint.get('raster-elevation') === 0.0; } _handleSpecialPaintPropertyUpdate(name: string) { if (name === 'raster-color' || name === 'raster-color-range') { - this._updateColorRamp(); + // Force recomputation + this._curRampRange = [NaN, NaN]; + + this.updateColorRamp(); } } - _updateColorRamp() { + updateColorRamp(overrideRange: ?[number, number]) { if (!this.hasColorMap()) return; + if (!this._curRampRange) return; const expression = this._transitionablePaint._values['raster-color'].value.expression; - const [start, end] = this._transitionablePaint._values['raster-color-range'].value.expression.evaluate({zoom: 0}); + const [start, end] = overrideRange || this._transitionablePaint._values['raster-color-range'].value.expression.evaluate({zoom: 0}) || [NaN, NaN]; + + if (isNaN(start) && isNaN(end)) return; + if (start === this._curRampRange[0] && end === this._curRampRange[1]) return; this.colorRamp = renderColorRamp({ expression, @@ -68,8 +89,8 @@ class RasterStyleLayer extends StyleLayer { resolution: COLOR_RAMP_RES, }); this.colorRampTexture = null; + this._curRampRange = [start, end]; } } -export {COLOR_RAMP_RES}; export default RasterStyleLayer; diff --git a/src/style/style_layer/raster_style_layer_properties.js b/src/style/style_layer/raster_style_layer_properties.js index 6fb30167335..4fd6e78ab56 100644 --- a/src/style/style_layer/raster_style_layer_properties.js +++ b/src/style/style_layer/raster_style_layer_properties.js @@ -1,4 +1,4 @@ -// This file is generated. Edit build/generate-style-code.js, then run `yarn run codegen`. +// This file is generated. Edit build/generate-style-code.js, then run `npm run codegen`. // @flow /* eslint-disable */ @@ -37,6 +37,9 @@ export type PaintProps = {| "raster-contrast": DataConstantProperty, "raster-resampling": DataConstantProperty<"linear" | "nearest">, "raster-fade-duration": DataConstantProperty, + "raster-emissive-strength": DataConstantProperty, + "raster-array-band": DataConstantProperty, + "raster-elevation": DataConstantProperty, |}; const paint: Properties = new Properties({ @@ -51,6 +54,9 @@ const paint: Properties = new Properties({ "raster-contrast": new DataConstantProperty(styleSpec["paint_raster"]["raster-contrast"]), "raster-resampling": new DataConstantProperty(styleSpec["paint_raster"]["raster-resampling"]), "raster-fade-duration": new DataConstantProperty(styleSpec["paint_raster"]["raster-fade-duration"]), + "raster-emissive-strength": new DataConstantProperty(styleSpec["paint_raster"]["raster-emissive-strength"]), + "raster-array-band": new DataConstantProperty(styleSpec["paint_raster"]["raster-array-band"]), + "raster-elevation": new DataConstantProperty(styleSpec["paint_raster"]["raster-elevation"]), }); // Note: without adding the explicit type annotation, Flow infers weaker types diff --git a/src/style/style_layer/sky_style_layer.js b/src/style/style_layer/sky_style_layer.js index 8e1155eed97..7600fa8386d 100644 --- a/src/style/style_layer/sky_style_layer.js +++ b/src/style/style_layer/sky_style_layer.js @@ -4,6 +4,10 @@ import StyleLayer from '../style_layer.js'; import properties from './sky_style_layer_properties.js'; import {Transitionable, Transitioning, PossiblyEvaluated} from '../properties.js'; import {renderColorRamp} from '../../util/color_ramp.js'; +import {warnOnce, degToRad} from '../../util/util.js'; +import {vec3, quat} from 'gl-matrix'; +import assert from 'assert'; + import type {PaintProps} from './sky_style_layer_properties.js'; import type Texture from '../../render/texture.js'; import type Painter from '../../render/painter.js'; @@ -12,11 +16,7 @@ import type Framebuffer from '../../gl/framebuffer.js'; import type {RGBAImage} from '../../util/image.js'; import type SkyboxGeometry from '../../render/skybox_geometry.js'; import type {Position} from '../../util/util.js'; -import {warnOnce, degToRad} from '../../util/util.js'; -import {vec3, quat} from 'gl-matrix'; -import assert from 'assert'; - -import type {Expression} from '../../style-spec/expression/expression.js'; +import type {ConfigOptions} from '../properties.js'; function getCelestialDirection(azimuth: number, altitude: number, leftHanded: boolean) { const up = [0, 0, 1]; @@ -44,8 +44,8 @@ class SkyLayer extends StyleLayer { skyboxGeometry: SkyboxGeometry; - constructor(layer: LayerSpecification, options?: ?Map) { - super(layer, properties, options); + constructor(layer: LayerSpecification, scope: string, options?: ?ConfigOptions) { + super(layer, properties, scope, options); this._updateColorRamp(); } diff --git a/src/style/style_layer/sky_style_layer_properties.js b/src/style/style_layer/sky_style_layer_properties.js index c0693a5e5ea..bc2898b7c6e 100644 --- a/src/style/style_layer/sky_style_layer_properties.js +++ b/src/style/style_layer/sky_style_layer_properties.js @@ -1,4 +1,4 @@ -// This file is generated. Edit build/generate-style-code.js, then run `yarn run codegen`. +// This file is generated. Edit build/generate-style-code.js, then run `npm run codegen`. // @flow /* eslint-disable */ diff --git a/src/style/style_layer/slot_style_layer.js b/src/style/style_layer/slot_style_layer.js index b58862c7534..2a68d7080e5 100644 --- a/src/style/style_layer/slot_style_layer.js +++ b/src/style/style_layer/slot_style_layer.js @@ -7,8 +7,8 @@ import properties from './slot_style_layer_properties.js'; import type {LayerSpecification} from '../../style-spec/types.js'; class SlotStyleLayer extends StyleLayer { - constructor(layer: LayerSpecification, _: mixed) { - super(layer, properties); + constructor(layer: LayerSpecification, scope: string, _: mixed) { + super(layer, properties, scope); } } diff --git a/src/style/style_layer/slot_style_layer_properties.js b/src/style/style_layer/slot_style_layer_properties.js index fa692c1d330..a593c437422 100644 --- a/src/style/style_layer/slot_style_layer_properties.js +++ b/src/style/style_layer/slot_style_layer_properties.js @@ -1,4 +1,4 @@ -// This file is generated. Edit build/generate-style-code.js, then run `yarn run codegen`. +// This file is generated. Edit build/generate-style-code.js, then run `npm run codegen`. // @flow /* eslint-disable */ diff --git a/src/style/style_layer/symbol_style_layer.js b/src/style/style_layer/symbol_style_layer.js index 39563b419e3..5891a8d347a 100644 --- a/src/style/style_layer/symbol_style_layer.js +++ b/src/style/style_layer/symbol_style_layer.js @@ -1,14 +1,19 @@ // @flow +import {mat4} from 'gl-matrix'; + import StyleLayer from '../style_layer.js'; import assert from 'assert'; import SymbolBucket from '../../data/bucket/symbol_bucket.js'; import resolveTokens from '../../util/resolve_tokens.js'; import properties from './symbol_style_layer_properties.js'; +import {computeColorAdjustmentMatrix} from '../../util/util.js'; + import type {FormattedSection} from '../../style-spec/expression/types/formatted.js'; import type {FormattedSectionExpression} from '../../style-spec/expression/definitions/format.js'; import type {CreateProgramParams} from "../../render/painter.js"; +import type {ConfigOptions} from '../properties.js'; import { Transitionable, @@ -49,8 +54,16 @@ class SymbolStyleLayer extends StyleLayer { _transitioningPaint: Transitioning; paint: PossiblyEvaluated; - constructor(layer: LayerSpecification, options?: ?Map) { - super(layer, properties, options); + _colorAdjustmentMatrix: Float32Array; + _saturation: number; + _contrast: number; + _brightnessMin: number; + _brightnessMax: number; + + constructor(layer: LayerSpecification, scope: string, options?: ?ConfigOptions) { + super(layer, properties, scope, options); + // $FlowFixMe[incompatible-type] + this._colorAdjustmentMatrix = mat4.identity([]); } recalculate(parameters: EvaluationParameters, availableImages: Array) { @@ -99,6 +112,22 @@ class SymbolStyleLayer extends StyleLayer { this._setPaintOverrides(); } + getColorAdjustmentMatrix(saturation: number, contrast: number, brightnessMin: number, brightnessMax: number): Float32Array { + if (this._saturation !== saturation || + this._contrast !== contrast || + this._brightnessMin !== brightnessMin || + this._brightnessMax !== brightnessMax) { + + this._colorAdjustmentMatrix = computeColorAdjustmentMatrix(saturation, contrast, brightnessMin, brightnessMax); + + this._saturation = saturation; + this._contrast = contrast; + this._brightnessMin = brightnessMin; + this._brightnessMax = brightnessMax; + } + return this._colorAdjustmentMatrix; + } + getValueAndResolveTokens(name: any, feature: Feature, canonical: CanonicalTileID, availableImages: Array): string { const value = this.layout.get(name).evaluate(feature, {}, canonical, availableImages); const unevaluated = this._unevaluatedLayout._values[name]; @@ -131,7 +160,7 @@ class SymbolStyleLayer extends StyleLayer { } const overriden = this.paint.get(overridable); const override = new FormatSectionOverride(overriden); - const styleExpression = new StyleExpression(override, overriden.property.specification); + const styleExpression = new StyleExpression(override, overriden.property.specification, this.scope, this.options); let expression = null; // eslint-disable-next-line no-warning-comments // TODO: check why were the `isLightConstant` values omitted from the construction of these expressions diff --git a/src/style/style_layer/symbol_style_layer_properties.js b/src/style/style_layer/symbol_style_layer_properties.js index 80cd147fab3..417f1c3cd0f 100644 --- a/src/style/style_layer/symbol_style_layer_properties.js +++ b/src/style/style_layer/symbol_style_layer_properties.js @@ -1,4 +1,4 @@ -// This file is generated. Edit build/generate-style-code.js, then run `yarn run codegen`. +// This file is generated. Edit build/generate-style-code.js, then run `npm run codegen`. // @flow /* eslint-disable */ @@ -131,6 +131,10 @@ export type PaintProps = {| "text-halo-blur": DataDrivenProperty, "text-translate": DataConstantProperty<[number, number]>, "text-translate-anchor": DataConstantProperty<"map" | "viewport">, + "icon-color-saturation": DataConstantProperty, + "icon-color-contrast": DataConstantProperty, + "icon-color-brightness-min": DataConstantProperty, + "icon-color-brightness-max": DataConstantProperty, |}; const paint: Properties = new Properties({ @@ -151,6 +155,10 @@ const paint: Properties = new Properties({ "text-halo-blur": new DataDrivenProperty(styleSpec["paint_symbol"]["text-halo-blur"]), "text-translate": new DataConstantProperty(styleSpec["paint_symbol"]["text-translate"]), "text-translate-anchor": new DataConstantProperty(styleSpec["paint_symbol"]["text-translate-anchor"]), + "icon-color-saturation": new DataConstantProperty(styleSpec["paint_symbol"]["icon-color-saturation"]), + "icon-color-contrast": new DataConstantProperty(styleSpec["paint_symbol"]["icon-color-contrast"]), + "icon-color-brightness-min": new DataConstantProperty(styleSpec["paint_symbol"]["icon-color-brightness-min"]), + "icon-color-brightness-max": new DataConstantProperty(styleSpec["paint_symbol"]["icon-color-brightness-max"]), }); // Note: without adding the explicit type annotation, Flow infers weaker types diff --git a/src/style/style_layer/typed_style_layer.js b/src/style/style_layer/typed_style_layer.js index 64f5d070e86..53b07dc7514 100644 --- a/src/style/style_layer/typed_style_layer.js +++ b/src/style/style_layer/typed_style_layer.js @@ -6,10 +6,12 @@ import type FillExtrusionStyleLayer from './fill_extrusion_style_layer.js'; import type HeatmapStyleLayer from './heatmap_style_layer.js'; import type LineStyleLayer from './line_style_layer.js'; import type SymbolStyleLayer from './symbol_style_layer.js'; +import type ModelStyleLayer from '../../../3d-style/style/style_layer/model_style_layer.js'; export type TypedStyleLayer = CircleStyleLayer | FillStyleLayer | FillExtrusionStyleLayer | HeatmapStyleLayer | LineStyleLayer | - SymbolStyleLayer; + SymbolStyleLayer | + ModelStyleLayer; diff --git a/src/style/style_layer_index.js b/src/style/style_layer_index.js index 18927ceaa9c..3de07363430 100644 --- a/src/style/style_layer_index.js +++ b/src/style/style_layer_index.js @@ -7,7 +7,7 @@ import groupByLayout from '../style-spec/group_by_layout.js'; import type {TypedStyleLayer} from './style_layer/typed_style_layer.js'; import type {LayerSpecification} from '../style-spec/types.js'; -import type {Expression} from '../style-spec/expression/expression.js'; +import type {ConfigOptions} from './properties.js'; export type LayerConfigs = {[_: string]: LayerSpecification }; export type Family = Array; @@ -19,29 +19,30 @@ class StyleLayerIndex { _layerConfigs: LayerConfigs; _layers: {[_: string]: TypedStyleLayer }; - _options: ?Map; + _options: ?ConfigOptions; constructor(layerConfigs: ?Array) { this.keyCache = {}; + this._layers = {}; + this._layerConfigs = {}; if (layerConfigs) { this.replace(layerConfigs); } } - replace(layerConfigs: Array, options?: ?Map) { + replace(layerConfigs: Array, options?: ?ConfigOptions) { this._layerConfigs = {}; this._layers = {}; this.update(layerConfigs, [], options); } - update(layerConfigs: Array, removedIds: Array, options: ?Map) { + update(layerConfigs: Array, removedIds: Array, options: ?ConfigOptions) { this._options = options; for (const layerConfig of layerConfigs) { this._layerConfigs[layerConfig.id] = layerConfig; - const layer = this._layers[layerConfig.id] = ((createStyleLayer(layerConfig, this._options): any): TypedStyleLayer); - layer.setScope(this.scope); + const layer = this._layers[layerConfig.id] = ((createStyleLayer(layerConfig, this.scope, this._options): any): TypedStyleLayer); layer.compileFilter(); if (this.keyCache[layerConfig.id]) delete this.keyCache[layerConfig.id]; diff --git a/src/style/terrain.js b/src/style/terrain.js index e1b1b3c0485..8356ec93080 100644 --- a/src/style/terrain.js +++ b/src/style/terrain.js @@ -5,7 +5,7 @@ import {Evented} from '../util/evented.js'; import {Properties, Transitionable, Transitioning, PossiblyEvaluated, DataConstantProperty} from './properties.js'; import EvaluationParameters from './evaluation_parameters.js'; -import type {TransitionParameters} from './properties.js'; +import type {ConfigOptions, TransitionParameters} from './properties.js'; import type {TerrainSpecification} from '../style-spec/types.js'; import {ZoomDependentExpression} from '../style-spec/expression/index.js'; @@ -31,24 +31,21 @@ class Terrain extends Evented { properties: PossiblyEvaluated; drapeRenderMode: number; - constructor(terrainOptions: TerrainSpecification, drapeRenderMode: number) { + constructor(terrainOptions: TerrainSpecification, drapeRenderMode: number, scope: string, configOptions?: ?ConfigOptions) { super(); - this._transitionable = new Transitionable(properties); - this.set(terrainOptions); + this.scope = scope; + this._transitionable = new Transitionable(properties, scope, configOptions); + this._transitionable.setTransitionOrValue(terrainOptions, configOptions); this._transitioning = this._transitionable.untransitioned(); this.drapeRenderMode = drapeRenderMode; } - setScope(scope: string) { - this.scope = scope; - } - get(): TerrainSpecification { return (this._transitionable.serialize(): any); } - set(terrain: TerrainSpecification) { - this._transitionable.setTransitionOrValue(terrain); + set(terrain: TerrainSpecification, configOptions?: ?ConfigOptions) { + this._transitionable.setTransitionOrValue(terrain, configOptions); } updateTransitions(parameters: TransitionParameters) { diff --git a/src/symbol/collision_index.js b/src/symbol/collision_index.js index 2e827b8f209..052aaa252d6 100644 --- a/src/symbol/collision_index.js +++ b/src/symbol/collision_index.js @@ -14,7 +14,8 @@ import * as symbolProjection from '../symbol/projection.js'; import type Transform from '../geo/transform.js'; import type Projection from '../geo/projection/projection.js'; -import type SymbolBucket, {SingleCollisionBox} from '../data/bucket/symbol_bucket.js'; +import type SymbolBucket from '../data/bucket/symbol_bucket.js'; +import type {SingleCollisionBox} from '../data/bucket/symbol_bucket.js'; import type {GlyphOffsetArray, SymbolLineVertexArray, PlacedSymbol} from '../data/array_types.js'; import type {FogState} from '../style/fog_helpers.js'; import type {Vec3, Mat4} from 'gl-matrix'; diff --git a/src/symbol/placement.js b/src/symbol/placement.js index 6a37be42efc..19cce649125 100644 --- a/src/symbol/placement.js +++ b/src/symbol/placement.js @@ -10,23 +10,26 @@ import {getAnchorAlignment, WritingMode} from './shaping.js'; import {mat4} from 'gl-matrix'; import assert from 'assert'; import Point from '@mapbox/point-geometry'; +import {getSymbolPlacementTileProjectionMatrix} from '../geo/projection/projection_util.js'; +import BuildingIndex from '../source/building_index.js'; +import {warnOnce} from '../util/util.js'; + import type Transform from '../geo/transform.js'; import type StyleLayer from '../style/style_layer.js'; import type Tile from '../source/tile.js'; -import type SymbolBucket, {SymbolBuffers, CollisionArrays, SingleCollisionBox} from '../data/bucket/symbol_bucket.js'; +import type SymbolBucket from '../data/bucket/symbol_bucket.js'; +import type {SymbolBuffers, CollisionArrays, SingleCollisionBox} from '../data/bucket/symbol_bucket.js'; import type {CollisionBoxArray, CollisionVertexArray, SymbolInstance} from '../data/array_types.js'; import type FeatureIndex from '../data/feature_index.js'; -import {getSymbolPlacementTileProjectionMatrix} from '../geo/projection/projection_util.js'; import type {OverscaledTileID} from '../source/tile_id.js'; import type {TextAnchor} from './symbol_layout.js'; import type {FogState} from '../style/fog_helpers.js'; import type {Mat4} from 'gl-matrix'; import type {PlacedCollisionBox} from './collision_index.js'; -import BuildingIndex from '../source/building_index.js'; -import {warnOnce} from '../util/util.js'; +import type {ObjMap} from '../types/obj-map.js'; // PlacedCollisionBox with all fields optional -type PartialPlacedCollisionBox = $ObjMap() => ?V>; +type PartialPlacedCollisionBox = ObjMap() => ?V>; class OpacityState { opacity: number; diff --git a/src/symbol/projection.js b/src/symbol/projection.js index 62df7fd3767..01aec36191c 100644 --- a/src/symbol/projection.js +++ b/src/symbol/projection.js @@ -5,6 +5,10 @@ import Point from '@mapbox/point-geometry'; import {mat2, mat4, vec3, vec4} from 'gl-matrix'; import * as symbolSize from './symbol_size.js'; import {addDynamicAttributes, updateGlobeVertexNormal} from '../data/bucket/symbol_bucket.js'; +import {WritingMode} from '../symbol/shaping.js'; +import {CanonicalTileID, OverscaledTileID} from '../source/tile_id.js'; +import {calculateGlobeLabelMatrix} from '../geo/projection/globe_util.js'; + import type Projection from '../geo/projection/projection.js'; import type Painter from '../render/painter.js'; import type Transform from '../geo/transform.js'; @@ -16,11 +20,10 @@ import type { SymbolGlobeExtArray, PlacedSymbol } from '../data/array_types.js'; + import type {Mat4, Vec3, Vec4} from 'gl-matrix'; +import type {VecType} from '../types/vec-type.js'; -import {WritingMode} from '../symbol/shaping.js'; -import {CanonicalTileID, OverscaledTileID} from '../source/tile_id.js'; -import {calculateGlobeLabelMatrix} from '../geo/projection/globe_util.js'; export {updateLineLabels, hideGlyphs, getLabelPlaneMatrixForRendering, getLabelPlaneMatrixForPlacement, getGlCoordMatrix, project, projectClamped, getPerspectiveRatio, placeFirstAndLastGlyph, placeGlyphAlongLine, xyTransformMat4}; type PlacedGlyph = {| @@ -242,7 +245,7 @@ function updateLineLabels(bucket: SymbolBucket, glCoordMatrix: Float32Array, pitchWithMap: boolean, keepUpright: boolean, - getElevation: ?((p: Point) => Array), + getElevation: ?((p: Point) => VecType), tileID: OverscaledTileID) { const tr = painter.transform; @@ -367,7 +370,7 @@ function placeFirstAndLastGlyph( lineVertexArray: SymbolLineVertexArray, labelPlaneMatrix: Float32Array, projectionCache: ProjectionCache, - getElevation: ?((p: Point) => Array), + getElevation: ?((p: Point) => VecType), returnPathInTileCoords: ?boolean, projection: Projection, tileID: OverscaledTileID, @@ -422,7 +425,7 @@ function requiresOrientationChange(writingMode: number, flipState: number, dx: n return dx < 0 ? {needsFlipping: true} : null; } -function placeGlyphsAlongLine(symbol: PlacedSymbol, fontSize: number, flip: boolean, keepUpright: boolean, posMatrix: Float32Array, labelPlaneMatrix: Float32Array, glCoordMatrix: Float32Array, glyphOffsetArray: GlyphOffsetArray, lineVertexArray: SymbolLineVertexArray, dynamicLayoutVertexArray: SymbolDynamicLayoutArray, globeExtVertexArray: ?SymbolGlobeExtArray, anchorPoint: VecType, tileAnchorPoint: Point, projectionCache: ProjectionCache, aspectRatio: number, getElevation: ?((p: Point) => Array), projection: Projection, tileID: OverscaledTileID, pitchWithMap: boolean): PlacementStatus { +function placeGlyphsAlongLine(symbol: PlacedSymbol, fontSize: number, flip: boolean, keepUpright: boolean, posMatrix: Float32Array, labelPlaneMatrix: Float32Array, glCoordMatrix: Float32Array, glyphOffsetArray: GlyphOffsetArray, lineVertexArray: SymbolLineVertexArray, dynamicLayoutVertexArray: SymbolDynamicLayoutArray, globeExtVertexArray: ?SymbolGlobeExtArray, anchorPoint: VecType, tileAnchorPoint: Point, projectionCache: ProjectionCache, aspectRatio: number, getElevation: ?((p: Point) => VecType), projection: Projection, tileID: OverscaledTileID, pitchWithMap: boolean): PlacementStatus { const fontScale = fontSize / 24; const lineOffsetX = symbol.lineOffsetX * fontScale; const lineOffsetY = symbol.lineOffsetY * fontScale; @@ -507,7 +510,7 @@ function placeGlyphsAlongLine(symbol: PlacedSymbol, fontSize: number, flip: bool return {}; } -function elevatePointAndProject(p: Point, tileID: CanonicalTileID, posMatrix: Float32Array, projection: Projection, getElevation: ?((p: Point) => Array)) { +function elevatePointAndProject(p: Point, tileID: CanonicalTileID, posMatrix: Float32Array, projection: Projection, getElevation: ?((p: Point) => VecType)) { const {x, y, z} = projection.projectTilePoint(p.x, p.y, tileID); if (!getElevation) { return project(x, y, z, posMatrix); @@ -516,7 +519,7 @@ function elevatePointAndProject(p: Point, tileID: CanonicalTileID, posMatrix: Fl return project(x + dx, y + dy, z + dz, posMatrix); } -function projectTruncatedLineSegment(previousTilePoint: Point, currentTilePoint: Point, previousProjectedPoint: Vec3, minimumLength: number, projectionMatrix: Float32Array, getElevation: ?((p: Point) => Array), projection: Projection, tileID: CanonicalTileID): Vec3 { +function projectTruncatedLineSegment(previousTilePoint: Point, currentTilePoint: Point, previousProjectedPoint: Vec3, minimumLength: number, projectionMatrix: Float32Array, getElevation: ?((p: Point) => VecType), projection: Projection, tileID: CanonicalTileID): Vec3 { // We are assuming "previousTilePoint" won't project to a point within one unit of the camera plane // If it did, that would mean our label extended all the way out from within the viewport to a (very distant) // point near the plane of the camera. We wouldn't be able to render the label anyway once it crossed the @@ -542,7 +545,7 @@ function placeGlyphAlongLine( lineVertexArray: SymbolLineVertexArray, labelPlaneMatrix: Float32Array, projectionCache: ProjectionCache, - getElevation: ?((p: Point) => Array), + getElevation: ?((p: Point) => VecType), returnPathInTileCoords: ?boolean, endGlyph: ?boolean, reprojection: Projection, diff --git a/src/symbol/shaping.js b/src/symbol/shaping.js index c20ff438a9a..e39bb585ab5 100644 --- a/src/symbol/shaping.js +++ b/src/symbol/shaping.js @@ -14,7 +14,6 @@ import {warnOnce} from '../util/util.js'; import type {StyleGlyph, GlyphMetrics} from '../style/style_glyph.js'; import {GLYPH_PBF_BORDER} from '../style/parse_glyph_pbf.js'; import type {ImagePosition} from '../render/image_atlas.js'; -import {IMAGE_PADDING} from '../render/image_atlas.js'; import type {GlyphRect, GlyphPositions} from '../render/glyph_atlas.js'; import Formatted, {FormattedSection} from '../style-spec/expression/types/formatted.js'; @@ -683,7 +682,7 @@ function shapeLines(shaping: Shaping, metrics = {width: size[0], height: size[1], - left: IMAGE_PADDING, + left: 0, top: -GLYPH_PBF_BORDER, advance: vertical ? size[1] : size[0], localGlyph: false}; diff --git a/src/symbol/symbol_layout.js b/src/symbol/symbol_layout.js index a55e97f5184..5b1ccb92f35 100644 --- a/src/symbol/symbol_layout.js +++ b/src/symbol/symbol_layout.js @@ -14,10 +14,13 @@ import { import findPoleOfInaccessibility from '../util/find_pole_of_inaccessibility.js'; import classifyRings from '../util/classify_rings.js'; import EXTENT from '../style-spec/data/extent.js'; -import SymbolBucket from '../data/bucket/symbol_bucket.js'; import EvaluationParameters from '../style/evaluation_parameters.js'; import {SIZE_PACK_FACTOR} from './symbol_size.js'; import ONE_EM from './one_em.js'; +import Point from '@mapbox/point-geometry'; +import murmur3 from 'murmurhash-js'; + +import type SymbolBucket from '../data/bucket/symbol_bucket.js'; import type {CanonicalTileID} from '../source/tile_id.js'; import type {Shaping, PositionedIcon, TextJustify} from './shaping.js'; import type {CollisionBoxArray} from '../data/array_types.js'; @@ -31,9 +34,6 @@ import type {PossiblyEvaluatedPropertyValue} from '../style/properties.js'; import type Projection from '../geo/projection/projection.js'; import type {Vec3} from 'gl-matrix'; -import Point from '@mapbox/point-geometry'; -import murmur3 from 'murmurhash-js'; - // The symbol layout process needs `text-size` evaluated at up to five different zoom levels, and // `icon-size` at up to three: // @@ -67,6 +67,16 @@ const baselineOffset = 7; const INVALID_TEXT_OFFSET = Number.POSITIVE_INFINITY; const sqrt2 = Math.sqrt(2); +export const SymbolBucketConstants = { + // this constant is based on the size of StructArray indexes used in a symbol + // bucket--namely, glyphOffsetArrayStart + // eg the max valid UInt16 is 65,535 + // See https://github.com/mapbox/mapbox-gl-js/issues/2907 for motivation + // lineStartIndex and textBoxStartIndex could potentially be concerns + // but we expect there to be many fewer boxes/lines than glyphs + MAX_GLYPHS: 65535 +}; + export function evaluateVariableOffset(anchor: TextAnchor, [offsetX, offsetY]: [number, number]): [number, number] { let x = 0, y = 0; @@ -862,7 +872,7 @@ function addSymbol(bucket: SymbolBucket, collisionCircleDiameter = getCollisionCircleHeight(verticalIconCircle, collisionCircleDiameter); const useRuntimeCollisionCircles = (collisionCircleDiameter > -1) ? 1 : 0; - if (bucket.glyphOffsetArray.length >= SymbolBucket.MAX_GLYPHS) warnOnce( + if (bucket.glyphOffsetArray.length >= SymbolBucketConstants.MAX_GLYPHS) warnOnce( "Too many glyphs being rendered in a tile. See https://github.com/mapbox/mapbox-gl-js/issues/2907" ); diff --git a/src/terrain/draw_terrain_raster.js b/src/terrain/draw_terrain_raster.js index 8a6afef741b..8032e8d2273 100644 --- a/src/terrain/draw_terrain_raster.js +++ b/src/terrain/draw_terrain_raster.js @@ -4,7 +4,6 @@ import DepthMode from '../gl/depth_mode.js'; import CullFaceMode from '../gl/cull_face_mode.js'; import {terrainRasterUniformValues} from './terrain_raster_program.js'; import {globeRasterUniformValues} from './globe_raster_program.js'; -import {Terrain} from './terrain.js'; import Tile from '../source/tile.js'; import assert from 'assert'; import {easeCubicInOut} from '../util/util.js'; @@ -30,6 +29,7 @@ import { import extend from '../style-spec/util/extend.js'; import type Program from '../render/program.js'; import type VertexBuffer from "../gl/vertex_buffer.js"; +import type {Terrain} from './terrain.js'; import {calculateGroundShadowFactor} from "../../3d-style/render/shadow_renderer.js"; import {getCutoffParams} from '../render/cutoff.js'; @@ -207,9 +207,9 @@ function drawTerrainForGlobe(painter: Painter, terrain: Terrain, sourceCache: So const gridMatrix = getGridMatrix(coord.canonical, tileBounds, latitudinalLod, tr.worldSize / tr._pixelsPerMercatorPixel); const normalizeMatrix = globeNormalizeECEF(globeTileBounds(coord.canonical)); const uniformValues = globeRasterUniformValues( - tr.projMatrix, globeMatrix, globeMercatorMatrix, normalizeMatrix, globeToMercatorTransition(tr.zoom), + tr.expandedFarZProjMatrix, globeMatrix, globeMercatorMatrix, normalizeMatrix, globeToMercatorTransition(tr.zoom), mercatorCenter, tr.frustumCorners.TL, tr.frustumCorners.TR, tr.frustumCorners.BR, - tr.frustumCorners.BL, tr.globeCenterInViewSpace, tr.globeRadius, viewport, skirtHeightValue, gridMatrix); + tr.frustumCorners.BL, tr.globeCenterInViewSpace, tr.globeRadius, viewport, skirtHeightValue, tr._farZ, gridMatrix); setShaderMode(coord, shaderMode); if (!program) { @@ -257,9 +257,9 @@ function drawTerrainForGlobe(painter: Painter, terrain: Terrain, sourceCache: So const drawPole = (program: Program, vertexBuffer: VertexBuffer) => program.draw( painter, gl.TRIANGLES, depthMode, StencilMode.disabled, colorMode, CullFaceMode.disabled, - globeRasterUniformValues(tr.projMatrix, poleMatrix, poleMatrix, normalizeMatrix, 0.0, mercatorCenter, + globeRasterUniformValues(tr.expandedFarZProjMatrix, poleMatrix, poleMatrix, normalizeMatrix, 0.0, mercatorCenter, tr.frustumCorners.TL, tr.frustumCorners.TR, tr.frustumCorners.BR, tr.frustumCorners.BL, - tr.globeCenterInViewSpace, tr.globeRadius, viewport, 0), "globe_pole_raster", vertexBuffer, + tr.globeCenterInViewSpace, tr.globeRadius, viewport, 0, tr._farZ), "globe_pole_raster", vertexBuffer, indexBuffer, segment); terrain.setupElevationDraw(tile, program, elevationOptions); diff --git a/src/terrain/elevation.js b/src/terrain/elevation.js index edfca2d3388..3fd9422b991 100644 --- a/src/terrain/elevation.js +++ b/src/terrain/elevation.js @@ -2,16 +2,17 @@ import MercatorCoordinate, {mercatorZfromAltitude} from '../geo/mercator_coordinate.js'; import DEMData from '../data/dem_data.js'; -import SourceCache from '../source/source_cache.js'; import {number as interpolate} from '../style-spec/util/interpolate.js'; import EXTENT from '../style-spec/data/extent.js'; import {vec3} from 'gl-matrix'; import Point from '@mapbox/point-geometry'; import {OverscaledTileID} from '../source/tile_id.js'; +import type {Vec4, Vec3} from 'gl-matrix'; +import type SourceCache from '../source/source_cache.js'; import type Projection from '../geo/projection/projection.js'; import type Tile from '../source/tile.js'; -import type {Vec3} from 'gl-matrix'; +import type {VecType} from '../types/vec-type.js'; /** * Options common to {@link Map#queryTerrainElevation} and {@link Map#unproject3d}, used to control how elevation @@ -118,8 +119,8 @@ export class Elevation { (tileID.canonical.y + y / EXTENT) / tilesAtTileZoom)); } - getAtTileOffsetFunc(tileID: OverscaledTileID, lat: number, worldSize: number, projection: Projection): Function { - return (p => { + getAtTileOffsetFunc(tileID: OverscaledTileID, lat: number, worldSize: number, projection: Projection): (Point) => VecType { + return ((p: Point) => { const elevation = this.getAtTileOffset(tileID, p.x, p.y); const upVector = projection.upVector(tileID.canonical, p.x, p.y); const upVectorScale = projection.upVectorScale(tileID.canonical, lat, worldSize).metersToTile; @@ -203,7 +204,7 @@ export class Elevation { * @param {vec3} dir The ray direction. * @param {number} exaggeration The terrain exaggeration. */ - raycast(position: Vec3, dir: Vec3, exaggeration: number): ?number { + raycast(_position: Vec3, _dir: Vec3, _exaggeration: number): ?number { throw new Error('Pure virtual method called.'); } @@ -212,10 +213,11 @@ export class Elevation { * Helper function that wraps `raycast`. * * @param {Point} screenPoint Screen point in pixels in top-left origin coordinate system. - * @returns {vec3} If there is intersection with terrain, returns 3D MercatorCoordinate's of - * intersection, as vec3(x, y, z), otherwise null. - */ /* eslint no-unused-vars: ["error", { "args": "none" }] */ - pointCoordinate(screenPoint: Point): ?Vec3 { + * @returns {vec4} If there is intersection with terrain, returns vec4(x, y, z, e), a + * 3D MercatorCoordinate's of intersection in its first 3 components, and elevation in meter in its 4th coordinate. + * Otherwise returns null. + */ + pointCoordinate(_screenPoint: Point): ?Vec4 { throw new Error('Pure virtual method called.'); } @@ -261,6 +263,35 @@ export class Elevation { get visibleDemTiles(): Array { throw new Error('Getter must be implemented in subclass.'); } + + /** + * Get elevation minimum and maximum for tiles which are visible on the current frame. + */ + getMinMaxForVisibleTiles(): ?{min: number, max: number} { + const visibleTiles = this.visibleDemTiles; + if (visibleTiles.length === 0) { + return null; + } + + let found = false; + let min = Number.MAX_VALUE; + let max = Number.MIN_VALUE; + for (const tile of visibleTiles) { + const minmax = this.getMinMaxForTile(tile.tileID); + if (!minmax) { + continue; + } + min = Math.min(min, minmax.min); + max = Math.max(max, minmax.max); + found = true; + } + + if (!found) { + return null; + } + + return {min, max}; + } } /** diff --git a/src/terrain/globe_raster_program.js b/src/terrain/globe_raster_program.js index 3af0a8f0913..3e8c9981ce5 100644 --- a/src/terrain/globe_raster_program.js +++ b/src/terrain/globe_raster_program.js @@ -24,6 +24,7 @@ export type GlobeRasterUniformsType = {| 'u_image0': Uniform1i, 'u_grid_matrix': UniformMatrix3f, 'u_skirt_height': Uniform1f, + 'u_far_z_cutoff': Uniform1f, 'u_frustum_tl': Uniform3f, 'u_frustum_tr': Uniform3f, 'u_frustum_br': Uniform3f, @@ -58,6 +59,7 @@ const globeRasterUniforms = (context: Context): GlobeRasterUniformsType => ({ 'u_image0': new Uniform1i(context), 'u_grid_matrix': new UniformMatrix3f(context), 'u_skirt_height': new Uniform1f(context), + 'u_far_z_cutoff': new Uniform1f(context), 'u_frustum_tl': new Uniform3f(context), 'u_frustum_tr': new Uniform3f(context), 'u_frustum_br': new Uniform3f(context), @@ -97,6 +99,7 @@ const globeRasterUniformValues = ( globeRadius: number, viewport: [number, number], skirtHeight: number, + farZCutoff: number, gridMatrix: ?Mat4 ): UniformValues => ({ 'u_proj_matrix': Float32Array.from(projMatrix), @@ -114,7 +117,8 @@ const globeRasterUniformValues = ( 'u_globe_radius': globeRadius, 'u_viewport': viewport, 'u_grid_matrix': gridMatrix ? Float32Array.from(gridMatrix) : new Float32Array(9), - 'u_skirt_height': skirtHeight + 'u_skirt_height': skirtHeight, + 'u_far_z_cutoff': farZCutoff }); const atmosphereUniformValues = ( diff --git a/src/terrain/terrain.js b/src/terrain/terrain.js index 652f76d8fae..fbb8fb75af4 100644 --- a/src/terrain/terrain.js +++ b/src/terrain/terrain.js @@ -7,8 +7,7 @@ import posAttributes from '../data/pos_attributes.js'; import {TriangleIndexArray, PosArray} from '../data/array_types.js'; import SegmentVector from '../data/segment.js'; import Texture from '../render/texture.js'; -import Program from '../render/program.js'; -import {Uniform1i, Uniform1f, Uniform2f, Uniform3f, Uniform4f, UniformMatrix4f} from '../render/uniform_binding.js'; +import {Uniform1i, Uniform1f, Uniform2f, Uniform3f, UniformMatrix4f} from '../render/uniform_binding.js'; import {prepareDEMTexture} from '../render/draw_hillshade.js'; import EXTENT from '../style-spec/data/extent.js'; import {clamp, warnOnce} from '../util/util.js'; @@ -18,7 +17,6 @@ import getWorkerPool from '../util/global_worker_pool.js'; import Dispatcher from '../util/dispatcher.js'; import GeoJSONSource from '../source/geojson_source.js'; import ImageSource from '../source/image_source.js'; -import RasterDEMTileSource from '../source/raster_dem_tile_source.js'; import RasterTileSource from '../source/raster_tile_source.js'; import VectorTileSource from '../source/vector_tile_source.js'; import Color from '../style-spec/util/color.js'; @@ -29,6 +27,7 @@ import {drawTerrainRaster, drawTerrainDepth} from './draw_terrain_raster.js'; import type RasterStyleLayer from '../style/style_layer/raster_style_layer.js'; import type CustomStyleLayer from '../style/style_layer/custom_style_layer.js'; import type LineStyleLayer from '../style/style_layer/line_style_layer.js'; +import type Program from '../render/program.js'; import {Elevation} from './elevation.js'; import Framebuffer from '../gl/framebuffer.js'; import ColorMode from '../gl/color_mode.js'; @@ -37,7 +36,6 @@ import CullFaceMode from '../gl/cull_face_mode.js'; import {clippingMaskUniformValues} from '../render/program/clipping_mask_program.js'; import MercatorCoordinate, {mercatorZfromAltitude} from '../geo/mercator_coordinate.js'; import browser from '../util/browser.js'; -import DEMData from '../data/dem_data.js'; import {DrapeRenderMode} from '../style/terrain.js'; import rasterFade from '../render/raster_fade.js'; import {create as createSource} from '../source/source.js'; @@ -46,7 +44,7 @@ import {globeMetersToEcef} from '../geo/projection/globe_util.js'; import {ZoomDependentExpression} from '../style-spec/expression/index.js'; import {number as interpolate} from '../style-spec/util/interpolate.js'; -import type Map from '../ui/map.js'; +import type {Map} from '../ui/map.js'; import type Painter from '../render/painter.js'; import type Style from '../style/style.js'; import type StyleLayer from '../style/style_layer.js'; @@ -55,7 +53,6 @@ import type IndexBuffer from '../gl/index_buffer.js'; import type Context from '../gl/context.js'; import type {UniformValues} from '../render/uniform_binding.js'; import type Transform from '../geo/transform.js'; -import type {DEMEncoding} from '../data/dem_data.js'; import type {Vec3, Vec4} from 'gl-matrix'; import type {CanonicalTileID} from '../source/tile_id.js'; @@ -244,8 +241,26 @@ export class Terrain extends Elevation { _pendingGroundEffectLayers: Array; framebufferCopyTexture: ?Texture; + _debugParams: { + sortTilesHiZFirst: boolean, + disableRenderCache: boolean + } + constructor(painter: Painter, style: Style) { super(); + + this._debugParams = {sortTilesHiZFirst: true, disableRenderCache: false}; + painter.tp.registerParameter(this._debugParams, ["Terrain"], "sortTilesHiZFirst", {}, () => { + this._style.map.triggerRepaint(); + }); + painter.tp.registerParameter(this._debugParams, ["Terrain"], "disableRenderCache", {}, () => { + this._style.map.triggerRepaint(); + }); + painter.tp.registerButton(["Terrain"], "Invalidate Render Cache", () => { + this.invalidateRenderCache = true; + this._style.map.triggerRepaint(); + }); + this.painter = painter; this.terrainTileForTile = {}; this.prevTerrainTileForTile = {}; @@ -663,18 +678,10 @@ export class Terrain extends Elevation { const min = this._getLoadedAreaMinimum(); const getTextureParameters = () => { - if (this.painter.terrainUseFloatDEM()) { - const image = new Float32Image( - {width: 1, height: 1}, - new Float32Array([min])); - return [gl.R32F, image]; - } else { - const image = new RGBAImage( - {width: 1, height: 1}, - new Uint8Array(DEMData.pack(min, ((this.sourceCache.getSource(): any): RasterDEMTileSource).encoding)) - ); - return [gl.RGBA, image]; - } + const image = new Float32Image( + {width: 1, height: 1}, + new Float32Array([min])); + return [gl.R32F, image]; }; const [internalFormat, image] = getTextureParameters(); @@ -703,7 +710,7 @@ export class Terrain extends Elevation { }) { const context = this.painter.context; const gl = context.gl; - const uniforms = defaultTerrainUniforms(((this.sourceCache.getSource(): any): RasterDEMTileSource).encoding); + const uniforms = defaultTerrainUniforms(); uniforms['u_exaggeration'] = this.exaggeration(); @@ -729,7 +736,7 @@ export class Terrain extends Elevation { return gl.NEAREST; } - return this.painter.terrainUseFloatDEM() ? gl.LINEAR : gl.NEAREST; + return this.painter.linearFloatFilteringSupported() ? gl.LINEAR : gl.NEAREST; }; const setDemSizeUniform = (demTexture: Texture) => { @@ -1027,7 +1034,7 @@ export class Terrain extends Elevation { fb.depthAttachment.set(this._sharedDepthStencil); } - if (context.extTextureFilterAnisotropic && !context.extTextureFilterAnisotropicForceOff) { + if (context.extTextureFilterAnisotropic) { gl.texParameterf(gl.TEXTURE_2D, context.extTextureFilterAnisotropic.TEXTURE_MAX_ANISOTROPY_EXT, context.extTextureFilterAnisotropicMax); @@ -1043,6 +1050,10 @@ export class Terrain extends Elevation { } _shouldDisableRenderCache(): boolean { + if (this._debugParams.disableRenderCache) { + return true; + } + // Disable render caches on dynamic events due to fading or transitioning. if (this._style.hasLightTransitions()) { return true; @@ -1440,6 +1451,7 @@ export class Terrain extends Elevation { } } let hasOverlap = false; + const proxiesToSort = new Set(); for (let i = 0; i < sourceCoords.length; i++) { const tile = sourceCache.getTile(sourceCoords[i]); if (!tile || !tile.hasData()) continue; @@ -1451,15 +1463,24 @@ export class Terrain extends Elevation { if (!array) { this.proxyToSource[proxy.tileID.key][sourceCache.id] = [id]; } else { - // The last element is parent added in loop above. This way we get - // a list in Z descending order which is needed for stencil masking. array.splice(array.length - 1, 0, id); } + const arr = this.proxyToSource[proxy.tileID.key][sourceCache.id]; + if (!proxiesToSort.has(arr)) { + proxiesToSort.add(arr); + } coords.push(id); hasOverlap = true; } } this._sourceTilesOverlap[sourceCache.id] = hasOverlap; + if (hasOverlap && this._debugParams.sortTilesHiZFirst) { + for (const arr of proxiesToSort) { + arr.sort((a, b) => { + return b.overscaledZ - a.overscaledZ; + }); + } + } } _setupProxiedCoordsForImageSource(sourceCache: SourceCache, sourceCoords: Array, previousProxyToSource: {[number]: {[string]: Array}}) { @@ -1706,7 +1727,6 @@ function createGrid(count: number): [PosArray, TriangleIndexArray, number] { export type TerrainUniformsType = {| 'u_dem': Uniform1i, 'u_dem_prev': Uniform1i, - 'u_dem_unpack': Uniform4f, 'u_dem_tl': Uniform2f, 'u_dem_scale': Uniform1f, 'u_dem_tl_prev': Uniform2f, @@ -1723,7 +1743,6 @@ export type TerrainUniformsType = {| export const terrainUniforms = (context: Context): TerrainUniformsType => ({ 'u_dem': new Uniform1i(context), 'u_dem_prev': new Uniform1i(context), - 'u_dem_unpack': new Uniform4f(context), 'u_dem_tl': new Uniform2f(context), 'u_dem_scale': new Uniform1f(context), 'u_dem_tl_prev': new Uniform2f(context), @@ -1737,11 +1756,10 @@ export const terrainUniforms = (context: Context): TerrainUniformsType => ({ 'u_label_plane_matrix_inv': new UniformMatrix4f(context), }); -function defaultTerrainUniforms(encoding: DEMEncoding): UniformValues { +function defaultTerrainUniforms(): UniformValues { return { 'u_dem': 2, 'u_dem_prev': 4, - 'u_dem_unpack': DEMData.getUnpackVector(encoding), 'u_dem_tl': [0, 0], 'u_dem_tl_prev': [0, 0], 'u_dem_scale': 0, diff --git a/src/tracked-parameters/internal/tracked_parameters_mock.js b/src/tracked-parameters/internal/tracked_parameters_mock.js new file mode 100644 index 00000000000..4e98aafd0fd --- /dev/null +++ b/src/tracked-parameters/internal/tracked_parameters_mock.js @@ -0,0 +1,27 @@ +// @flow + +import type {ITrackedParameters} from 'tracked_parameters_proxy'; + +// Parameter description +// * Description from TweakPane +// * extended options: +// * noSave - do not save/load the parameter value between page reloads +export type Description = Object | { noSave: boolean }; + +export function registerParameter(_containerObject: Object, _scope: Array, _name: string, _description: ?Description, _changeValueCallback: ?Function) { +} + +export function registerButton(_scope: Array, _buttonTitle: string, _onClick: Function) { +} + +export function registerBinding(_containerObject: Object, _scope: Array, _name: string, _description: ?Object) { +} + +export function refreshUI() {} + +export class TrackedParameters implements ITrackedParameters { + registerParameter(_containerObject: Object, _scope: Array, _name: string, _description: ?Description, _changeValueCallback: ?Function) { } + registerButton(_scope: Array, _buttonTitle: string, _onClick: Function) { } + registerBinding(_containerObject: Object, _scope: Array, _name: string, _description: ?Object) { } + refreshUI() { } +} diff --git a/src/tracked-parameters/internal/tracked_parameters_ui.js b/src/tracked-parameters/internal/tracked_parameters_ui.js new file mode 100644 index 00000000000..3b690f19214 --- /dev/null +++ b/src/tracked-parameters/internal/tracked_parameters_ui.js @@ -0,0 +1,583 @@ +// @flow + +import {Pane} from 'tweakpane'; +import cloneDeep from 'lodash.clonedeep'; +import serialize from 'serialize-to-js'; +import assert from 'assert'; +import {isWorker, warnOnce} from '../../../src/util/util.js'; +import type {Map as MapboxMap} from '../../../src/ui/map.js'; +import type {Description} from './tracked_parameters_mock.js'; +import type {ITrackedParameters} from 'tracked_parameters_proxy'; + +if (!isWorker()) { + const style = document.createElement('style'); + style.innerHTML = ` + .tp-fldv_t { + white-space: pre; + } + .tp-lblv_l { + white-space: pre; + } + .mapbox-devtools::-webkit-scrollbar { + width: 10px; + height: 10px; + } + .mapbox-devtools::-webkit-scrollbar-track { + background: rgba(0, 0, 0, 0.2); + border-radius: 10px; + } + .mapbox-devtools::-webkit-scrollbar-thumb { + background: rgba(110, 110, 110); + border-radius: 10px; + } + .mapbox-devtools::-webkit-scrollbar-thumb:hover { + background-color: rgba(90, 90, 90); + } + .mapboxgl-ctrl.mapbox-devtools { + max-height: 75vh; + overflow-y: auto; + } + .mapboxgl-ctrl.mapbox-devtools button.tp-btnv_b:hover { + background-color: var(--btn-bg-h); + } + `; + + if (document.head) { + document.head.appendChild(style); + } +} + +function deserialize(serialized: string): Object { + return [eval][0](`(${serialized})`); +} + +// Serializable folder state +class FolderState { + isFolded: boolean; + current: Object; + + constructor() { + this.isFolded = false; + this.current = {}; + } +} + +// Serializable whole debug pane state +class PaneState { + isMainPaneShown: boolean; + folders: Map; + scrollTopRatio: number; + + constructor() { + this.scrollTopRatio = 0; + this.isMainPaneShown = false; + this.folders = new Map (); + } +} + +function mergePaneParams(dest: PaneState, src: PaneState) { + if (src.isMainPaneShown !== undefined) { + dest.isMainPaneShown = src.isMainPaneShown; + } + + const mergedFolderKeys = [...new Set([...src.folders.keys(), ...dest.folders.keys()])]; + + for (const key of mergedFolderKeys) { + const srcFolder = src.folders.get(key); + const destFolder = dest.folders.get(key); + if (srcFolder && destFolder) { + for (const parameterKey of Object.keys(srcFolder.current)) { + destFolder.current[parameterKey] = cloneDeep(srcFolder.current[parameterKey]); + } + } else if (srcFolder) { + dest.folders.set(key, srcFolder); + } + } +} + +function deSerializePaneParams(input: ?string): PaneState { + let obj = {}; + if (input) { + try { + obj = deserialize(input); + } catch (err) { + console.log(`Tracked parameters deserialization error: ${err}`); + } + } + + const p = new PaneState(); + + // Replace properties if present + if ('isMainPaneShown' in obj) { + p.isMainPaneShown = obj.isMainPaneShown; + } + + if ('scrollTopRatio' in obj) { + p.scrollTopRatio = obj.scrollTopRatio; + } + + if ('folders' in obj) { + if (obj.folders instanceof Map) { + obj.folders.forEach((it, key) => { + const f = new FolderState(); + if (`isFolded` in it) { + f.isFolded = it.isFolded; + } + if (`current` in it && it.current instanceof Object) { + f.current = cloneDeep(it.current); + } + p.folders.set(key, f); + }); + } + } + return p; +} + +// For fast prototyping in case of only one map present +let global: ?TrackedParameters; + +export function registerParameter(object: Object, scope: Array, name: string, description: ?Description, onChange: Function) { + if (global) { + global.registerParameter(object, scope, name, description, onChange); + + console.warn(`Dev only "registerParameter('${name}')" call. For production consider replacing with tracked parameters container method.`); + } +} + +export function registerButton(scope: Array, buttonTitle: string, onClick: Function) { + if (global) { + global.registerButton(scope, buttonTitle, onClick); + + console.warn(`Dev only "registerButton('${buttonTitle}')" call. For production consider replacing with tracked parameters container method.`); + } +} + +export function registerBinding(containerObject: Object, scope: Array, name: string, description: ?Object) { + if (global) { + global.registerBinding(containerObject, scope, name, description); + + console.warn(`Dev only "registerBinding('${name}')" call. For production consider replacing with tracked parameters container method.`); + } +} + +export function refreshUI() { + if (global) { + global.refreshUI(); + + warnOnce(`Dev only "refreshUI" call. For production consider replacing with tracked parameters container method.`); + } +} + +// Reference to actual object and default values +class ParameterInfo { + containerObject: Object; + parameterName: string; + defaultValue: any; + noSave: boolean; + tpBinding: any; + + constructor(object: Object, parameterName: string, defaultValue: any, noSave: boolean, tpBinding: any) { + this.containerObject = object; + this.parameterName = parameterName; + this.defaultValue = defaultValue; + this.noSave = noSave; + this.tpBinding = tpBinding; + } +} + +// Tracked parameters container +export class TrackedParameters implements ITrackedParameters { + _map: MapboxMap; + _container: HTMLElement; + + // All TweakPane scopes + _folders: Map; + + // For (de)serialization + _paneState: PaneState; + + // Store container object reference for each parameter + // Key = Scopes + parameter name + _parametersInfo: Map; + + _storageName: string; + + _scrollUnblocked: boolean; + + constructor(map: MapboxMap) { + this._map = map; + this._folders = new Map (); + + const id = map._getMapId(); + const url = new URL(window.location.pathname, window.location.href).toString(); + this._storageName = `TP_${id}_${url}`; + + this._parametersInfo = new Map(); + + this._scrollUnblocked = false; + + this.initPane(); + + // Keep global reference, making it possible to register parameters + // without passing reference to TrackedParameters class + // Needed purely for dev purposes where only one map is present + global = this; + } + + // Serialize pane state and write it to local storage + dump() { + if (this._scrollUnblocked) { + const scrollTop = this._container.scrollTop; + this._paneState.scrollTopRatio = scrollTop / this._container.scrollHeight; + } + + const serialized = serialize(this._paneState); + localStorage.setItem(this._storageName, serialized); + } + + unfold() { + this._paneState.folders.forEach((folderState) => { + folderState.isFolded = false; + }); + + this._folders.forEach((folder) => { + folder.expanded = true; + folder.refresh(); + }); + + this.dump(); + } + + resetToDefaults() { + const doReset = () => { + this._parametersInfo.forEach((elem, key) => { + elem.containerObject[elem.parameterName] = cloneDeep(elem.defaultValue); + + // Update serializable state as well + const folderName = key.slice(0, key.lastIndexOf("|")); + const folder = this._paneState.folders.get(folderName); + if (folder) { + folder.current[elem.parameterName] = cloneDeep(elem.defaultValue); + } + }); + this.checkDefaults(); + this._folders.forEach((folder) => { + folder.refresh(); + }); + }; + + // Workaround for tweakpane bug (int vs float color storage) + doReset(); + doReset(); + + this.dump(); + } + + checkDefaults() { + const folderModCount = new Map(); + + for (const key of this._folders.keys()) { + folderModCount.set(key, 0); + } + + this._parametersInfo.forEach((parameterInfo, key) => { + const isDefault = JSON.stringify(parameterInfo.defaultValue) === JSON.stringify(parameterInfo.containerObject[parameterInfo.parameterName]); + + const noSaveIndicator = parameterInfo.noSave ? "❗💾 " : ""; + parameterInfo.tpBinding.label = (isDefault ? " " : "* ") + noSaveIndicator + parameterInfo.parameterName; + + const folderName = key.slice(0, key.lastIndexOf("|")); + + let scopes = folderName.split("_"); + scopes = scopes.slice(1, scopes.length); + + let folderIterName = ""; + for (const scope of scopes) { + folderIterName += `_${scope}`; + if (!isDefault) { + const prevCount = folderModCount.get(folderIterName); + if (prevCount !== undefined) { + folderModCount.set(folderIterName, prevCount + 1); + } + } + } + }); + + folderModCount.forEach((count, key) => { + const folder = this._folders.get(key); + if (folder) { + if (key === "_") { + return; + } + const folderName = key.slice(key.lastIndexOf("_") + 1, key.length); + if (count === 0) { + folder.title = ` ${folderName}`; + } else { + folder.title = `* ${folderName}`; + } + } + }); + + } + + saveParameters() { + if (!("showSaveFilePicker" in window)) { + alert("File System Access API not supported, consider switching to recent versions of Chrome"); + return; + } + + const opts = { + types: [ + { + description: "Parameters file", + accept: {"text/plain": [".params"]} + }, + ], + }; + window.showSaveFilePicker(opts).then((fileHandle) => { + return fileHandle.createWritable(); + }).then((writable) => { + const serialized = serialize(this._paneState); + return Promise.all([writable, writable.write(serialized)]); + }).then(([writable, _]) => { + writable.close(); + }).catch((err) => { + console.error(err); + }); + } + + loadParameters() { + if (!("showSaveFilePicker" in window)) { + alert("File System Access API not supported, consider switching to recent versions of chrome"); + return; + } + + const opts = { + types: [ + { + description: "Parameters file", + accept: {"text/plain": [".params"]} + }, + ], + }; + window.showOpenFilePicker(opts).then((fileHandles) => { + return fileHandles[0].getFile(); + }).then((file) => { + return file.text(); + }).then((fileData) => { + const loadedPaneState = deSerializePaneParams(fileData); + + mergePaneParams(this._paneState, loadedPaneState); + + this._paneState.folders.forEach((folder, folderKey) => { + for (const [parameterKey, value] of Object.entries(folder.current)) { + const fullParameterName = `${folderKey}|${parameterKey}`; + const paramInfo = this._parametersInfo.get(fullParameterName); + if (paramInfo && !paramInfo.noSave) { + paramInfo.containerObject[parameterKey] = cloneDeep(value); + } + } + + const tpFolder = this._folders.get(folderKey); + if (tpFolder) { + tpFolder.expanded = !folder.isFolded; + } + }); + + this.checkDefaults(); + + this._folders.forEach((folder) => { + folder.refresh(); + }); + }).catch((err) => { + console.error(err); + }); + } + + initPane() { + // Load state + const serializedPaneState = localStorage.getItem(this._storageName); + this._paneState = deSerializePaneParams(serializedPaneState); + + // Create containers for UI elements + this._container = window.document.createElement('div'); + this._container.className = 'mapboxgl-ctrl mapbox-devtools'; + + this._container.onwheel = () => { + this._scrollUnblocked = true; + this.dump(); + }; + + this._container.onclick = () => { + this._scrollUnblocked = true; + this.dump(); + }; + + this._container.onscroll = () => { + if (this._scrollUnblocked) { + this.dump(); + } + }; + + this._container.onscrollend = () => { + if (this._scrollUnblocked) { + this.dump(); + } + }; + + const positionContainer = this._map._controlPositions['top-right']; + positionContainer.appendChild(this._container); + + const pane = new Pane({ + container: this._container, + expanded: this._paneState.isMainPaneShown, + title: 'devtools', + }); + + pane.on('fold', (e) => { + this._paneState.isMainPaneShown = e.expanded; + this.dump(); + }); + + pane.addButton({ + title: 'Reset To Defaults' + }).on('click', () => { + this.resetToDefaults(); + }); + + pane.addButton({ + title: 'Unfold' + }).on('click', () => { + this.unfold(); + }); + + pane.addButton({ + title: 'Save' + }).on('click', () => { + this.saveParameters(); + }); + + pane.addButton({ + title: 'Load' + }).on('click', () => { + this.loadParameters(); + }); + + this._folders.set("_", pane); + } + + createFoldersChainAndSelectScope(scope: Array): {currentScope: any, fullScopeName: string } { + assert(scope.length >= 1); + + // Iterate/create panes + let currentScope: any = this._folders.get("_"); + let fullScopeName = "_"; + for (let i = 0; i < scope.length; ++i) { + fullScopeName = scope.slice(0, i + 1).reduce((prev, cur) => { return `${prev}_${cur}`; }, "_"); + + if (this._folders.has(fullScopeName)) { + currentScope = this._folders.get(fullScopeName); + } else { + const folder = currentScope.addFolder({ + title: ` ${scope[i]}`, + expanded: true, + }); + + this._folders.set(fullScopeName, folder); + currentScope = folder; + + if (!this._paneState.folders.has(fullScopeName)) { + const folderObj = new FolderState(); + this._paneState.folders.set(fullScopeName, folderObj); + } + + const folderObj: FolderState = (this._paneState.folders.get(fullScopeName): any); + currentScope.expanded = !folderObj.isFolded; + + currentScope.on('fold', (ev) => { + folderObj.isFolded = !ev.expanded; + this.dump(); + }); + + } + } + + return {currentScope, fullScopeName}; + } + + registerParameter(containerObject: Object, scope: Array, name: string, description: ?Description, changeValueCallback: ?Function) { + const {currentScope, fullScopeName} = this.createFoldersChainAndSelectScope(scope); + + const folderStateObj: FolderState = (this._paneState.folders.get(fullScopeName): any); + + // Full parameter name with scope prefix + const fullParameterName = `${fullScopeName}|${name}`; + + if (!this._parametersInfo.has(fullParameterName)) { + const defaultValue = cloneDeep(containerObject[name]); + + // Check if parameter should ignore (de)serialization + const noSave = !!(description && description.noSave); + + if (!noSave && folderStateObj.current.hasOwnProperty(name)) { + containerObject[name] = cloneDeep(folderStateObj.current[name]); + } else { + folderStateObj.current[name] = cloneDeep(containerObject[name]); + } + + // Create binding to TweakPane UI + const binding = currentScope.addBinding(containerObject, name, description); + binding.on('change', (ev) => { + folderStateObj.current[name] = cloneDeep(ev.value); + this.dump(); + this.checkDefaults(); + if (changeValueCallback) { changeValueCallback(ev.value); } + }); + + this._parametersInfo.set(fullParameterName, new ParameterInfo(containerObject, name, defaultValue, noSave, binding)); + } else { + console.log(`Parameter "${fullParameterName}" already registered`); + } + + this.checkDefaults(); + + if (!this._scrollUnblocked) { + this._container.scrollTop = this._paneState.scrollTopRatio * this._container.scrollHeight; + } + } + + registerButton(scope: Array, buttonTitle: string, onClick: Function) { + const {currentScope} = this.createFoldersChainAndSelectScope(scope); + + // Add button to TweakPane UI + const button = currentScope.addButton({title: buttonTitle}); + button.on('click', () => { + onClick(); + }); + } + + registerBinding(containerObject: Object, scope: Array, name: string, description: ?Object) { + const {currentScope} = this.createFoldersChainAndSelectScope(scope); + + const modifiedLabel = ` ${(() => { + if (!description) { + return ""; + } + + if ("label" in description) { + return description.label; + } + + return name; + })()}`; + + // Add button to TweakPane UI + currentScope.addBinding(containerObject, name, {...description, label: modifiedLabel}); + } + + refreshUI() { + this._folders.forEach((folder) => { + folder.refresh(); + }); + } +} diff --git a/src/tracked-parameters/tracked_parameters.js b/src/tracked-parameters/tracked_parameters.js new file mode 100644 index 00000000000..ba56c561b91 --- /dev/null +++ b/src/tracked-parameters/tracked_parameters.js @@ -0,0 +1,13 @@ +// @flow + +// Main module for tracked parameters usage +// - For prod builds module exports mocked function and classes +// - For non-prod builds without "devtools" map option exports mocked function and classes +// - For prod builds with "devtools" map option exports actual function and classes + +/* eslint-disable import/no-unresolved */ +/* $FlowFixMe[cannot-resolve-module] */ +import {TrackedParameters, registerParameter, registerButton, registerBinding, refreshUI} from 'tracked_parameters_proxy'; +import {TrackedParameters as TrackedParametersMock} from './internal/tracked_parameters_mock.js'; + +export {TrackedParameters, TrackedParametersMock, registerParameter, registerButton, registerBinding, refreshUI}; diff --git a/src/types/class.js b/src/types/class.js new file mode 100644 index 00000000000..75f646f6575 --- /dev/null +++ b/src/types/class.js @@ -0,0 +1,3 @@ +// @flow strict + +export type Class = Class; diff --git a/src/types/grid-index.js b/src/types/grid-index.js new file mode 100644 index 00000000000..9786f918923 --- /dev/null +++ b/src/types/grid-index.js @@ -0,0 +1,10 @@ +// @flow + +export interface GridIndex { + constructor(extent: number, n: number, padding: number): GridIndex; + constructor(data: ArrayBuffer): GridIndex; + + insert(key: number, x1: number, y1: number, x2: number, y2: number): void; + query(x1: number, y1: number, x2: number, y2: number, intersectionText?: (number, number, number, number) => boolean): Array; + toArrayBuffer(): ArrayBuffer; +} diff --git a/src/types/obj-map.js b/src/types/obj-map.js new file mode 100644 index 00000000000..9ac56d810d9 --- /dev/null +++ b/src/types/obj-map.js @@ -0,0 +1,3 @@ +// @flow strict + +export type ObjMap = $ObjMap; diff --git a/src/types/point-like.js b/src/types/point-like.js new file mode 100644 index 00000000000..39355f21e72 --- /dev/null +++ b/src/types/point-like.js @@ -0,0 +1,4 @@ +// @flow strict +import type Point from '@mapbox/point-geometry'; + +export type PointLike = Point | [number, number]; diff --git a/src/types/tilejson.js b/src/types/tilejson.js index 328f378e2a9..1be48bec883 100644 --- a/src/types/tilejson.js +++ b/src/types/tilejson.js @@ -1,7 +1,7 @@ // @flow export type TileJSON = {| - tilejson: '2.2.0' | '2.1.0' | '2.0.1' | '2.0.0' | '1.0.0', + tilejson: '3.0.0' | '2.2.0' | '2.1.0' | '2.0.1' | '2.0.0' | '1.0.0', name?: string, description?: string, version?: string, @@ -13,5 +13,7 @@ export type TileJSON = {| minzoom?: number, maxzoom?: number, bounds?: [number, number, number, number], - center?: [number, number, number] + center?: [number, number, number], + vector_layers?: Array; + raster_layers?: Array; |}; diff --git a/src/types/vec-type.js b/src/types/vec-type.js new file mode 100644 index 00000000000..bf5cbaed56b --- /dev/null +++ b/src/types/vec-type.js @@ -0,0 +1,3 @@ +// @flow strict + +export type VecType = Array | Float32Array | Float64Array; diff --git a/src/types/window.js b/src/types/window.js deleted file mode 100644 index 8091373745e..00000000000 --- a/src/types/window.js +++ /dev/null @@ -1,172 +0,0 @@ -// @flow strict - -/* global IDBEnvironment */ - -export interface Window extends EventTarget, IDBEnvironment { - +caches: CacheStorage; - +clientInformation: Navigator; - +closed: boolean; - defaultStatus: string; - +devicePixelRatio: number; - +document: Document; - +doNotTrack: string; - +frameElement: Element; - +frames: Window; - +history: History; - +innerHeight: number; - +innerWidth: number; - +isSecureContext: boolean; - +length: number; - +location: Location; - +origin: string; - name: string; - +navigator: Navigator; - offscreenBuffering: string | boolean; - onabort: (ev: UIEvent) => ?boolean; - onafterprint: (ev: Event) => ?boolean; - onbeforeprint: (ev: Event) => ?boolean; - onbeforeunload: (ev: Event) => ?boolean; - onblur: (ev: FocusEvent) => ?boolean; - oncanplay: (ev: Event) => ?boolean; - oncanplaythrough: (ev: Event) => ?boolean; - onchange: (ev: Event) => ?boolean; - onclick: (ev: MouseEvent) => ?boolean; - oncompassneedscalibration: (ev: Event) => ?boolean; - oncontextmenu: (ev: Event) => ?boolean; - ondblclick: (ev: MouseEvent) => ?boolean; - ondevicelight: (ev: Event) => ?boolean; - ondevicemotion: (ev: Event) => ?boolean; - ondeviceorientation: (ev: Event) => ?boolean; - ondrag: (ev: DragEvent) => ?boolean; - ondragend: (ev: DragEvent) => ?boolean; - ondragenter: (ev: DragEvent) => ?boolean; - ondragleave: (ev: DragEvent) => ?boolean; - ondragover: (ev: DragEvent) => ?boolean; - ondragstart: (ev: DragEvent) => ?boolean; - ondrop: (ev: DragEvent) => ?boolean; - ondurationchange: (ev: Event) => ?boolean; - onemptied: (ev: Event) => ?boolean; - onended: (ev: Event) => ?boolean; - onerror: (ev: Event) => ?boolean; - onfocus: (ev: FocusEvent) => ?boolean; - onhashchange: (ev: Event) => ?boolean; - oninput: (ev: Event) => ?boolean; - oninvalid: (ev: Event) => ?boolean; - onkeydown: (ev: KeyboardEvent) => ?boolean; - onkeypress: (ev: KeyboardEvent) => ?boolean; - onkeyup: (ev: KeyboardEvent) => ?boolean; - onload: (ev: Event) => ?boolean; - onloadeddata: (ev: Event) => ?boolean; - onloadedmetadata: (ev: Event) => ?boolean; - onloadstart: (ev: Event) => ?boolean; - onmessage: (ev: MessageEvent) => ?boolean; - onmousedown: (ev: MouseEvent) => ?boolean; - onmouseenter: (ev: MouseEvent) => ?boolean; - onmouseleave: (ev: MouseEvent) => ?boolean; - onmousemove: (ev: MouseEvent) => ?boolean; - onmouseout: (ev: MouseEvent) => ?boolean; - onmouseover: (ev: MouseEvent) => ?boolean; - onmouseup: (ev: MouseEvent) => ?boolean; - onmousewheel: (ev: WheelEvent) => ?boolean; - onoffline: (ev: Event) => ?boolean; - ononline: (ev: Event) => ?boolean; - onorientationchange: (ev: Event) => ?boolean; - onpagehide: (ev: Event) => ?boolean; - onpageshow: (ev: Event) => ?boolean; - onpause: (ev: Event) => ?boolean; - onplay: (ev: Event) => ?boolean; - onplaying: (ev: Event) => ?boolean; - onpopstate: (ev: Event) => ?boolean; - onprogress: (ev: ProgressEvent) => ?boolean; - onratechange: (ev: Event) => ?boolean; - onreadystatechange: (ev: ProgressEvent) => ?boolean; - onreset: (ev: Event) => ?boolean; - onresize: (ev: UIEvent) => ?boolean; - onscroll: (ev: UIEvent) => ?boolean; - onseeked: (ev: Event) => ?boolean; - onseeking: (ev: Event) => ?boolean; - onselect: (ev: UIEvent) => ?boolean; - onstalled: (ev: Event) => ?boolean; - onstorage: (ev: Event) => ?boolean; - onsubmit: (ev: Event) => ?boolean; - onsuspend: (ev: Event) => ?boolean; - ontimeupdate: (ev: Event) => ?boolean; - ontouchcancel: (ev: TouchEvent) => ?boolean; - ontouchend: (ev: TouchEvent) => ?boolean; - ontouchmove: (ev: TouchEvent) => ?boolean; - ontouchstart: (ev: TouchEvent) => ?boolean; - onunload: (ev: Event) => ?boolean; - onvolumechange: (ev: Event) => ?boolean; - onwaiting: (ev: Event) => ?boolean; - opener: Window; - orientation: string | number; - +outerHeight: number; - +outerWidth: number; - +pageXOffset: number; - +pageYOffset: number; - +parent: Window; - +performance: Performance; - +screen: Screen; - +screenLeft: number; - +screenTop: number; - +screenX: number; - +screenY: number; - +scrollX: number; - +scrollY: number; - +self: Window; - status: string; - +top: Window; - +window: Window; - - Blob: typeof Blob; - HTMLImageElement: typeof HTMLImageElement; - HTMLElement: typeof HTMLElement; - HTMLVideoElement: typeof HTMLVideoElement; - HTMLCanvasElement: typeof HTMLCanvasElement; - Image: typeof Image; - ImageData: typeof ImageData; - URL: typeof URL; - URLSearchParams: typeof URLSearchParams; - WebGLFramebuffer: typeof WebGLFramebuffer; - WheelEvent: typeof WheelEvent; - Worker: typeof Worker; - XMLHttpRequest: typeof XMLHttpRequest; - Request: typeof Request; - AbortController: typeof AbortController; - - alert(message?: string): void; - blur(): void; - captureEvents(): void; - close(): void; - confirm(message?: string): boolean; - focus(): void; - getComputedStyle(elt: Element, pseudoElt?: string): CSSStyleDeclaration; - getMatchedCSSRules(elt: Element, pseudoElt?: string): CSSRuleList; - getSelection(): Selection; - moveBy(x?: number, y?: number): void; - moveTo(x?: number, y?: number): void; - msWriteProfilerMark(profilerMarkName: string): void; - open(url?: string, target?: string, features?: string, replace?: boolean): Window; - postMessage(message: mixed, targetOrigin: string, transfer?: ArrayBuffer[]): void; - print(): void; - prompt(message?: string, _default?: string): string | null; - releaseEvents(): void; - resizeBy(x?: number, y?: number): void; - resizeTo(x?: number, y?: number): void; - scroll(x?: number, y?: number): void; - scrollBy(x?: number, y?: number): void; - scrollTo(x?: number, y?: number): void; - stop(): void; - - clearInterval(intervalId?: number): void; - clearTimeout(timeoutId?: number): void; - setTimeout(callback: () => void, ms?: number): number; - setInterval(callback: () => void, ms?: number): number; - - requestAnimationFrame(callback: (timestamp: number) => void): number; - cancelAnimationFrame(handle: number): void; - msRequestAnimationFrame(callback: (timestamp: number) => void): number; - msCancelAnimationFrame(handle: number): void; - webkitRequestAnimationFrame(callback: (timestamp: number) => void): number; - webkitCancelAnimationFrame(handle: number): void; -} diff --git a/src/ui/camera.js b/src/ui/camera.js index fd5e21854b7..07a24065c98 100644 --- a/src/ui/camera.js +++ b/src/ui/camera.js @@ -12,8 +12,12 @@ import { } from '../util/util.js'; import {number as interpolate} from '../style-spec/util/interpolate.js'; import browser from '../util/browser.js'; -import LngLat, {earthRadius} from '../geo/lng_lat.js'; -import LngLatBounds from '../geo/lng_lat_bounds.js'; +import LngLat, {earthRadius, latLngToECEF, ecefToLatLng, LngLatBounds} from '../geo/lng_lat.js'; +import { + GLOBE_RADIUS, + GLOBE_ZOOM_THRESHOLD_MAX, + GLOBE_ZOOM_THRESHOLD_MIN +} from '../geo/projection/globe_constants.js'; import Point from '@mapbox/point-geometry'; import {Event, Evented} from '../util/evented.js'; import assert from 'assert'; @@ -25,30 +29,23 @@ import MercatorCoordinate, { latFromMercatorY, lngFromMercatorX } from '../geo/mercator_coordinate.js'; -import { - latLngToECEF, - ecefToLatLng, - GLOBE_RADIUS, - GLOBE_ZOOM_THRESHOLD_MAX, - GLOBE_ZOOM_THRESHOLD_MIN -} from '../geo/projection/globe_util.js'; import {vec3, vec4, mat4} from 'gl-matrix'; import type {FreeCameraOptions} from './free_camera.js'; import type Transform from '../geo/transform.js'; -import type {LngLatLike} from '../geo/lng_lat.js'; -import type {LngLatBoundsLike} from '../geo/lng_lat_bounds.js'; +import type {LngLatLike, LngLatBoundsLike} from '../geo/lng_lat.js'; import type {ElevationQueryOptions} from '../terrain/elevation.js'; import type {TaskID} from '../util/task_queue.js'; import type {Callback} from '../types/callback.js'; -import type {PointLike} from '@mapbox/point-geometry'; +import type {PointLike} from '../types/point-like.js'; import {Aabb} from '../util/primitives.js'; import type {PaddingOptions} from '../geo/edge_insets.js'; import type {MapEvent} from './events.js'; +import type {ObjMap} from '../types/obj-map.js'; /** * A helper type: converts all Object type values to non-maybe types. */ -type Required = $ObjMap(v: V) => $NonMaybeType>; +type Required = ObjMap(v: V) => $NonMaybeType>; /** * Options common to {@link Map#jumpTo}, {@link Map#easeTo}, and {@link Map#flyTo}, controlling the desired location, @@ -59,15 +56,15 @@ type Required = $ObjMap(v: V) => $NonMaybeType>; * @property {LngLatLike} center The location to place at the screen center. * @property {number} zoom The desired zoom level. * @property {number} bearing The desired bearing in degrees. The bearing is the compass direction that - * is "up". For example, `bearing: 90` orients the map so that east is up. + * is "up". For example, `bearing: 90` orients the map so that east is up. * @property {number} pitch The desired pitch in degrees. The pitch is the angle towards the horizon - * measured in degrees with a range between 0 and 85 degrees. For example, pitch: 0 provides the appearance - * of looking straight down at the map, while pitch: 60 tilts the user's perspective towards the horizon. - * Increasing the pitch value is often used to display 3D objects. + * measured in degrees with a range between 0 and 85 degrees. For example, pitch: 0 provides the appearance + * of looking straight down at the map, while pitch: 60 tilts the user's perspective towards the horizon. + * Increasing the pitch value is often used to display 3D objects. * @property {LngLatLike} around The location serving as the origin for a change in `zoom`, `pitch` and/or `bearing`. - * This location will remain at the same screen position following the transform. - * This is useful for drawing attention to a location that is not in the screen center. - * `center` is ignored if `around` is included. + * This location will remain at the same screen position following the transform. + * This is useful for drawing attention to a location that is not in the screen center. + * `center` is ignored if `around` is included. * @property {PaddingOptions} padding Dimensions in pixels applied on each side of the viewport for shifting the vanishing point. * @example * // set the map's initial perspective with CameraOptions @@ -107,29 +104,29 @@ export type FullCameraOptions = { * @typedef {Object} AnimationOptions * @property {number} duration The animation's duration, measured in milliseconds. * @property {Function} easing A function taking a time in the range 0..1 and returning a number where 0 is - * the initial state and 1 is the final state. + * the initial state and 1 is the final state. * @property {PointLike} offset The target center's offset relative to real map container center at the end of animation. * @property {boolean} animate If `false`, no animation will occur. * @property {boolean} essential If `true`, then the animation is considered essential and will not be affected by - * [`prefers-reduced-motion`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion). + * [`prefers-reduced-motion`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion). * @property {boolean} preloadOnly If `true`, it will trigger tiles loading across the animation path, but no animation will occur. * @property {number} curve The zooming "curve" that will occur along the - * flight path. A high value maximizes zooming for an exaggerated animation, while a low - * value minimizes zooming for an effect closer to {@link Map#easeTo}. 1.42 is the average - * value selected by participants in the user study discussed in - * [van Wijk (2003)](https://www.win.tue.nl/~vanwijk/zoompan.pdf). A value of - * `Math.pow(6, 0.25)` would be equivalent to the root mean squared average velocity. A - * value of 1 would produce a circular motion. If `minZoom` is specified, this option will be ignored. + * flight path. A high value maximizes zooming for an exaggerated animation, while a low + * value minimizes zooming for an effect closer to {@link Map#easeTo}. 1.42 is the average + * value selected by participants in the user study discussed in + * [van Wijk (2003)](https://www.win.tue.nl/~vanwijk/zoompan.pdf). A value of + * `Math.pow(6, 0.25)` would be equivalent to the root mean squared average velocity. A + * value of 1 would produce a circular motion. If `minZoom` is specified, this option will be ignored. * @property {number} minZoom The zero-based zoom level at the peak of the flight path. If - * this option is specified, `curve` will be ignored. + * this option is specified, `curve` will be ignored. * @property {number} speed The average speed of the animation defined in relation to - * `curve`. A speed of 1.2 means that the map appears to move along the flight path - * by 1.2 times `curve` screenfuls every second. A _screenful_ is the map's visible span. - * It does not correspond to a fixed physical distance, but varies by zoom level. + * `curve`. A speed of 1.2 means that the map appears to move along the flight path + * by 1.2 times `curve` screenfuls every second. A _screenful_ is the map's visible span. + * It does not correspond to a fixed physical distance, but varies by zoom level. * @property {number} screenSpeed The average speed of the animation measured in screenfuls - * per second, assuming a linear timing curve. If `speed` is specified, this option is ignored. + * per second, assuming a linear timing curve. If `speed` is specified, this option is ignored. * @property {number} maxDuration The animation's maximum duration, measured in milliseconds. - * If duration exceeds maximum duration, it resets to 0. + * If duration exceeds maximum duration, it resets to 0. * @see [Example: Slowly fly to a location](https://docs.mapbox.com/mapbox-gl-js/example/flyto-options/) * @see [Example: Customize camera animations](https://docs.mapbox.com/mapbox-gl-js/example/camera-animation/) * @see [Example: Navigate the map with game-like controls](https://docs.mapbox.com/mapbox-gl-js/example/game-controls/) @@ -185,7 +182,6 @@ class Camera extends Evented { _zooming: boolean; _rotating: boolean; _pitching: boolean; - _padding: boolean; _bearingSnap: number; _easeStart: number; @@ -197,11 +193,6 @@ class Camera extends Evented { _onEaseEnd: ?(easeId?: string) => void; _easeFrameId: ?TaskID; - +_requestRenderFrame: (() => void) => TaskID; - +_cancelRenderFrame: (_: TaskID) => void; - - +_preloadTiles: (transform: Transform | Array, callback?: Callback) => any; - constructor(transform: Transform, options: {bearingSnap: number, respectPrefersReducedMotion?: boolean}) { super(); this._moving = false; @@ -472,7 +463,7 @@ class Camera extends Evented { * @memberof Map# * @param {number} bearing The desired bearing. * @param {EasingOptions | null} options Options describing the destination and animation of the transition. - * Accepts {@link CameraOptions} and {@link AnimationOptions}. + * Accepts {@link CameraOptions} and {@link AnimationOptions}. * @param {Object | null} eventData Additional properties to be added to event objects of events triggered by this method. * @fires Map.event:movestart * @fires Map.event:moveend @@ -494,7 +485,7 @@ class Camera extends Evented { * * @memberof Map# * @param {EasingOptions | null} options Options describing the destination and animation of the transition. - * Accepts {@link CameraOptions} and {@link AnimationOptions}. + * Accepts {@link CameraOptions} and {@link AnimationOptions}. * @param {Object | null} eventData Additional properties to be added to event objects of events triggered by this method. * @fires Map.event:movestart * @fires Map.event:moveend @@ -513,7 +504,7 @@ class Camera extends Evented { * * @memberof Map# * @param {EasingOptions | null} options Options describing the destination and animation of the transition. - * Accepts {@link CameraOptions} and {@link AnimationOptions}. + * Accepts {@link CameraOptions} and {@link AnimationOptions}. * @param {Object | null} eventData Additional properties to be added to event objects of events triggered by this method. * @fires Map.event:movestart * @fires Map.event:moveend @@ -537,7 +528,7 @@ class Camera extends Evented { * * @memberof Map# * @param {EasingOptions | null} options Options describing the destination and animation of the transition. - * Accepts {@link CameraOptions} and {@link AnimationOptions}. + * Accepts {@link CameraOptions} and {@link AnimationOptions}. * @param {Object | null} eventData Additional properties to be added to event objects of events triggered by this method. * @fires Map.event:movestart * @fires Map.event:moveend @@ -589,8 +580,8 @@ class Camera extends Evented { * * @memberof Map# * @param {LngLatBoundsLike} bounds Calculate the center for these bounds in the viewport and use - * the highest zoom level up to and including `Map#getMaxZoom()` that fits - * in the viewport. LngLatBounds represent a box that is always axis-aligned with bearing 0. + * the highest zoom level up to and including `Map#getMaxZoom()` that fits + * in the viewport. LngLatBounds represent a box that is always axis-aligned with bearing 0. * @param {CameraOptions | null} options Options object. * @param {number | PaddingOptions} [options.padding] The amount of padding in pixels to add to the given bounds. * @param {number} [options.bearing=0] Desired map bearing at end of animation, in degrees. @@ -598,7 +589,7 @@ class Camera extends Evented { * @param {PointLike} [options.offset=[0, 0]] The center of the given bounds relative to the map's center, measured in pixels. * @param {number} [options.maxZoom] The maximum zoom level to allow when the camera would transition to the specified bounds. * @returns {CameraOptions | void} If map is able to fit to provided bounds, returns `CameraOptions` with - * `center`, `zoom`, and `bearing`. If map is unable to fit, method will warn and return undefined. + * `center`, `zoom`, and `bearing`. If map is unable to fit, method will warn and return undefined. * @example * const bbox = [[-79, 43], [-73, 45]]; * const newCameraTransform = map.cameraForBounds(bbox, { @@ -710,6 +701,8 @@ class Camera extends Evented { aabb = Aabb.applyTransform(aabb, mat4.multiply([], worldToCamera, aabbOrientation)); + aabb = this._extendAABBWithPaddings(aabb, eOptions, tr, bearing); + vec3.transformMat4(center, center, worldToCamera); const aabbHalfExtentZ = (aabb.max[2] - aabb.min[2]) * 0.5; @@ -745,6 +738,42 @@ class Camera extends Evented { return {center: tr.center, zoom, bearing, pitch}; } + _extendAABBWithPaddings(aabb: Aabb, eOptions: FullCameraOptions, tr: Transform, bearing: number): Aabb { + const size = vec3.sub([], aabb.max, aabb.min); + + const screenPadL = tr.padding.left || 0; + const screenPadR = tr.padding.right || 0; + const screenPadB = tr.padding.bottom || 0; + const screenPadT = tr.padding.top || 0; + + const {left: padL, right: padR, top: padT, bottom: padB} = eOptions.padding; + + const halfScreenPadX = (screenPadL + screenPadR) * 0.5; + const halfScreenPadY = (screenPadT + screenPadB) * 0.5; + + const scaleX = (tr.width - (screenPadL + screenPadR + padL + padR)) / size[0]; + const scaleY = (tr.height - (screenPadB + screenPadT + padB + padT)) / size[1]; + + const zoomRef = Math.min(tr.scaleZoom(tr.scale * Math.min(scaleX, scaleY)), eOptions.maxZoom); + + const scaleRatio = tr.scale / tr.zoomScale(zoomRef); + + aabb = new Aabb( + [aabb.min[0] - (padL + halfScreenPadX) * scaleRatio, aabb.min[1] - (padB + halfScreenPadY) * scaleRatio, aabb.min[2]], + [aabb.max[0] + (padR + halfScreenPadX) * scaleRatio, aabb.max[1] + (padT + halfScreenPadY) * scaleRatio, aabb.max[2]]); + + const centerOffset = (typeof eOptions.offset.x === 'number' && typeof eOptions.offset.y === 'number') ? + new Point(eOptions.offset.x, eOptions.offset.y) : + Point.convert(eOptions.offset); + + const rotatedOffset = centerOffset.rotate(-degToRad(bearing)); + + aabb.center[0] -= rotatedOffset.x * scaleRatio; + aabb.center[1] += rotatedOffset.y * scaleRatio; + + return aabb; + } + /** @section {Querying features} */ /** @@ -757,7 +786,7 @@ class Camera extends Evented { * @param {LngLatLike} lnglat The geographical location at which to query. * @param {ElevationQueryOptions} [options] Options object. * @param {boolean} [options.exaggerated=true] When `true` returns the terrain elevation with the value of `exaggeration` from the style already applied. - * When `false`, returns the raw value of the underlying data without styling applied. + * When `false`, returns the raw value of the underlying data without styling applied. * @returns {number | null} The elevation in meters. * @example * const coordinate = [-122.420679, 37.772537]; @@ -778,6 +807,7 @@ class Camera extends Evented { * the highest zoom level up to and including `Map#getMaxZoom()` that fits * the points in the viewport at the specified bearing. * @memberof Map# + * @param transform The current transform * @param {LngLatLike} p0 First point * @param {LngLatLike} p1 Second point * @param {number} bearing Desired map bearing at end of animation, in degrees @@ -804,7 +834,6 @@ class Camera extends Evented { const tr = transform.clone(); const eOptions = this._extendCameraOptions(options); - const edgePadding = tr.padding; tr.bearing = bearing; tr.pitch = pitch; @@ -834,29 +863,9 @@ class Camera extends Evented { aabb = Aabb.applyTransform(aabb, worldToCamera); - const size = vec3.sub([], aabb.max, aabb.min); - - const screenPadL = edgePadding.left || 0; - const screenPadR = edgePadding.right || 0; - const screenPadB = edgePadding.bottom || 0; - const screenPadT = edgePadding.top || 0; - - const {left: padL, right: padR, top: padT, bottom: padB} = eOptions.padding; - - const halfScreenPadX = (screenPadL + screenPadR) * 0.5; - const halfScreenPadY = (screenPadT + screenPadB) * 0.5; - - const scaleX = (tr.width - (screenPadL + screenPadR + padL + padR)) / size[0]; - const scaleY = (tr.height - (screenPadB + screenPadT + padB + padT)) / size[1]; - - const zoomRef = Math.min(tr.scaleZoom(tr.scale * Math.min(scaleX, scaleY)), eOptions.maxZoom); - - const scaleRatio = tr.scale / tr.zoomScale(zoomRef); - - aabb = new Aabb( - [aabb.min[0] - (padL + halfScreenPadX) * scaleRatio, aabb.min[1] - (padB + halfScreenPadY) * scaleRatio, aabb.min[2]], - [aabb.max[0] + (padR + halfScreenPadX) * scaleRatio, aabb.max[1] + (padT + halfScreenPadY) * scaleRatio, aabb.max[2]]); + aabb = this._extendAABBWithPaddings(aabb, eOptions, tr, bearing); + const size = vec3.sub([], aabb.max, aabb.min); const aabbHalfExtentZ = size[2] * 0.5; const frustumDistance = this._minimumAABBFrustumDistance(tr, aabb); @@ -868,15 +877,6 @@ class Camera extends Evented { const offset = vec3.scale([], normalZ, frustumDistance + aabbHalfExtentZ); const cameraPosition = vec3.add([], aabb.center, offset); - const centerOffset = (typeof eOptions.offset.x === 'number' && typeof eOptions.offset.y === 'number') ? - new Point(eOptions.offset.x, eOptions.offset.y) : - Point.convert(eOptions.offset); - - const rotatedOffset = centerOffset.rotate(-degToRad(bearing)); - - aabb.center[0] -= rotatedOffset.x * scaleRatio; - aabb.center[1] += rotatedOffset.y * scaleRatio; - vec3.transformMat4(aabb.center, aabb.center, cameraToWorld); vec3.transformMat4(cameraPosition, cameraPosition, cameraToWorld); @@ -906,14 +906,14 @@ class Camera extends Evented { * * @memberof Map# * @param {LngLatBoundsLike} bounds Center these bounds in the viewport and use the highest - * zoom level up to and including `Map#getMaxZoom()` that fits them in the viewport. + * zoom level up to and including `Map#getMaxZoom()` that fits them in the viewport. * @param {Object} [options] Options supports all properties from {@link AnimationOptions} and {@link CameraOptions} in addition to the fields below. * @param {number | PaddingOptions} [options.padding] The amount of padding in pixels to add to the given bounds. * @param {number} [options.pitch=0] Desired map pitch at end of animation, in degrees. * @param {number} [options.bearing=0] Desired map bearing at end of animation, in degrees. * @param {boolean} [options.linear=false] If `true`, the map transitions using - * {@link Map#easeTo}. If `false`, the map transitions using {@link Map#flyTo}. See - * those functions and {@link AnimationOptions} for information about options available. + * {@link Map#easeTo}. If `false`, the map transitions using {@link Map#flyTo}. See + * those functions and {@link AnimationOptions} for information about options available. * @param {Function} [options.easing] An easing function for the animated transition. See {@link AnimationOptions}. * @param {PointLike} [options.offset=[0, 0]] The center of the given bounds relative to the map's center, measured in pixels. * @param {number} [options.maxZoom] The maximum zoom level to allow when the map view transitions to the specified bounds. @@ -943,11 +943,11 @@ class Camera extends Evented { * @param {PointLike} p1 Second point on screen, in pixel coordinates. * @param {number} bearing Desired map bearing at end of animation, in degrees. * @param {EasingOptions | null} options Options object. - * Accepts {@link CameraOptions} and {@link AnimationOptions}. + * Accepts {@link CameraOptions} and {@link AnimationOptions}. * @param {number | PaddingOptions} [options.padding] The amount of padding in pixels to add to the given bounds. * @param {boolean} [options.linear=false] If `true`, the map transitions using - * {@link Map#easeTo}. If `false`, the map transitions using {@link Map#flyTo}. See - * those functions and {@link AnimationOptions} for information about options available. + * {@link Map#easeTo}. If `false`, the map transitions using {@link Map#flyTo}. See + * those functions and {@link AnimationOptions} for information about options available. * @param {number} [options.pitch=0] Desired map pitch at end of animation, in degrees. * @param {Function} [options.easing] An easing function for the animated transition. See {@link AnimationOptions}. * @param {PointLike} [options.offset=[0, 0]] The center of the given bounds relative to the map's center, measured in pixels. @@ -1217,7 +1217,7 @@ class Camera extends Evented { * * @memberof Map# * @param {EasingOptions} options Options describing the destination and animation of the transition. - * Accepts {@link CameraOptions} and {@link AnimationOptions}. + * Accepts {@link CameraOptions} and {@link AnimationOptions}. * @param {Object | null} eventData Additional properties to be added to event objects of events triggered by this method. * @fires Map.event:movestart * @fires Map.event:zoomstart @@ -1365,7 +1365,6 @@ class Camera extends Evented { this._zooming = zoomChanged; this._rotating = bearingChanged; this._pitching = pitchChanged; - this._padding = paddingChanged; this._easeId = options.easeId; this._prepareEase(eventData, options.noMoveStart, currently); @@ -1430,7 +1429,6 @@ class Camera extends Evented { this._zooming = false; this._rotating = false; this._pitching = false; - this._padding = false; if (wasZooming) { this.fire(new Event('zoomend', eventData)); @@ -1455,25 +1453,25 @@ class Camera extends Evented { * * @memberof Map# * @param {Object} options Options describing the destination and animation of the transition. - * Accepts {@link CameraOptions}, {@link AnimationOptions}, - * and the following additional options. + * Accepts {@link CameraOptions}, {@link AnimationOptions}, + * and the following additional options. * @param {number} [options.curve=1.42] The zooming "curve" that will occur along the - * flight path. A high value maximizes zooming for an exaggerated animation, while a low - * value minimizes zooming for an effect closer to {@link Map#easeTo}. 1.42 is the average - * value selected by participants in the user study discussed in - * [van Wijk (2003)](https://www.win.tue.nl/~vanwijk/zoompan.pdf). A value of - * `Math.pow(6, 0.25)` would be equivalent to the root mean squared average velocity. A - * value of 1 would produce a circular motion. If `options.minZoom` is specified, this option will be ignored. + * flight path. A high value maximizes zooming for an exaggerated animation, while a low + * value minimizes zooming for an effect closer to {@link Map#easeTo}. 1.42 is the average + * value selected by participants in the user study discussed in + * [van Wijk (2003)](https://www.win.tue.nl/~vanwijk/zoompan.pdf). A value of + * `Math.pow(6, 0.25)` would be equivalent to the root mean squared average velocity. A + * value of 1 would produce a circular motion. If `options.minZoom` is specified, this option will be ignored. * @param {number} [options.minZoom] The zero-based zoom level at the peak of the flight path. If - * this option is specified, `options.curve` will be ignored. + * this option is specified, `options.curve` will be ignored. * @param {number} [options.speed=1.2] The average speed of the animation defined in relation to - * `options.curve`. A speed of 1.2 means that the map appears to move along the flight path - * by 1.2 times `options.curve` screenfuls every second. A _screenful_ is the map's visible span. - * It does not correspond to a fixed physical distance, but varies by zoom level. + * `options.curve`. A speed of 1.2 means that the map appears to move along the flight path + * by 1.2 times `options.curve` screenfuls every second. A _screenful_ is the map's visible span. + * It does not correspond to a fixed physical distance, but varies by zoom level. * @param {number} [options.screenSpeed] The average speed of the animation measured in screenfuls - * per second, assuming a linear timing curve. If `options.speed` is specified, this option is ignored. + * per second, assuming a linear timing curve. If `options.speed` is specified, this option is ignored. * @param {number} [options.maxDuration] The animation's maximum duration, measured in milliseconds. - * If duration exceeds maximum duration, it resets to 0. + * If duration exceeds maximum duration, it resets to 0. * @param {Object | null} eventData Additional properties to be added to event objects of events triggered by this method. * @fires Map.event:movestart * @fires Map.event:zoomstart @@ -1530,19 +1528,25 @@ class Camera extends Evented { const tr = this.transform, startZoom = this.getZoom(), startBearing = this.getBearing(), - startPitch = this.getPitch(), - startPadding = this.getPadding(); + startPitch = this.getPitch(); const zoom = 'zoom' in options ? clamp(+options.zoom, tr.minZoom, tr.maxZoom) : startZoom; const bearing = 'bearing' in options ? this._normalizeBearing(options.bearing, startBearing) : startBearing; const pitch = 'pitch' in options ? +options.pitch : startPitch; - const padding = 'padding' in options ? options.padding : tr.padding; const scale = tr.zoomScale(zoom - startZoom); const offsetAsPoint = Point.convert(options.offset); - let pointAtOffset = tr.centerPoint.add(offsetAsPoint); + const pointAtOffset = tr.centerPoint.add(offsetAsPoint); const locationAtOffset = tr.pointLocation(pointAtOffset); - const center = LngLat.convert(options.center || locationAtOffset); + + let center = options.center; + // Calculate center with respect to padding + if (center && options.padding) { + const easingOptions = this._cameraForBounds(this.transform, center, center, bearing, pitch, options); + if (easingOptions) center = easingOptions.center; + } + + center = LngLat.convert(center || locationAtOffset); this._normalizeCenter(center); const from = tr.project(locationAtOffset); @@ -1628,7 +1632,6 @@ class Camera extends Evented { const zoomChanged = true; const bearingChanged = (startBearing !== bearing); const pitchChanged = (pitch !== startPitch); - const paddingChanged = !tr.isPaddingEqual(padding); const frame = (tr: Transform) => (k: number) => { // s: The distance traveled along the flight path, measured in ρ-screenfuls. @@ -1642,12 +1645,6 @@ class Camera extends Evented { if (pitchChanged) { tr.pitch = interpolate(startPitch, pitch, k); } - if (paddingChanged) { - tr.interpolatePadding(startPadding, padding, k); - // When padding is being applied, Transform#centerPoint is changing continuously, - // thus we need to recalculate offsetPoint every frame - pointAtOffset = tr.centerPoint.add(offsetAsPoint); - } const newCenter = k === 1 ? center : tr.unproject(from.add(delta.mult(u(s))).mult(scale)); tr.setLocationAtPoint(tr.renderWorldCopies ? newCenter.wrap() : newCenter, pointAtOffset); @@ -1669,7 +1666,6 @@ class Camera extends Evented { this._zooming = zoomChanged; this._rotating = bearingChanged; this._pitching = pitchChanged; - this._padding = paddingChanged; this._prepareEase(eventData, false); this._ease(frame(tr), () => this._afterEase(eventData), options); @@ -1693,6 +1689,12 @@ class Camera extends Evented { return this._stop(); } + // $FlowFixMe[incompatible-return] - No-op in the Camera class, implemented by the Map class + _requestRenderFrame(_callback: () => void): TaskID {} + + // No-op in the Camera class, implemented by the Map class + _cancelRenderFrame(_: TaskID): void {} + _stop(allowGestures?: boolean, easeId?: string): this { if (this._easeFrameId) { this._cancelRenderFrame(this._easeFrameId); @@ -1757,7 +1759,9 @@ class Camera extends Evented { // interpolating between the two endpoints will cross it. _normalizeCenter(center: LngLat) { const tr = this.transform; - if (!tr.renderWorldCopies || tr.maxBounds) return; + if (tr.maxBounds) return; + const isGlobe = tr.projection.name === 'globe'; + if (!isGlobe && !tr.renderWorldCopies) return; const delta = center.lng - tr.center.lng; center.lng += @@ -1785,6 +1789,9 @@ class Camera extends Evented { return transforms; } + + // No-op in the Camera class, implemented by the Map class + _preloadTiles(_transform: Transform | Array, _callback?: Callback): any {} } // In debug builds, check that camera change events are fired in the correct order. diff --git a/src/ui/control/attribution_control.js b/src/ui/control/attribution_control.js index 3b37106995c..5cb0e7e1f74 100644 --- a/src/ui/control/attribution_control.js +++ b/src/ui/control/attribution_control.js @@ -5,7 +5,7 @@ import {bindAll} from '../../util/util.js'; import config from '../../util/config.js'; import {getHashString} from '../hash.js'; -import type Map, {ControlPosition} from '../map.js'; +import type {Map, ControlPosition} from '../map.js'; type Options = { compact?: boolean, @@ -162,7 +162,7 @@ class AttributionControl { this.styleId = stylesheet.id; } - const sourceCaches = this._map.style._sourceCaches; + const sourceCaches = this._map.style._mergedSourceCaches; for (const id in sourceCaches) { const sourceCache = sourceCaches[id]; if (sourceCache.used) { diff --git a/src/ui/control/fullscreen_control.js b/src/ui/control/fullscreen_control.js index a7104905a9c..512ad01f43f 100644 --- a/src/ui/control/fullscreen_control.js +++ b/src/ui/control/fullscreen_control.js @@ -3,9 +3,8 @@ import * as DOM from '../../util/dom.js'; import {bindAll, warnOnce} from '../../util/util.js'; -import window from '../../util/window.js'; -import type Map from '../map.js'; +import type {Map} from '../map.js'; type Options = { container?: HTMLElement @@ -32,10 +31,10 @@ class FullscreenControl { _fullscreenButton: HTMLElement; _container: HTMLElement; - constructor(options: Options) { + constructor(options?: Options) { this._fullscreen = false; if (options && options.container) { - if (options.container instanceof window.HTMLElement) { + if (options.container instanceof HTMLElement) { this._container = options.container; } else { warnOnce('Full screen control \'container\' must be a DOM element.'); @@ -45,9 +44,9 @@ class FullscreenControl { '_onClickFullscreen', '_changeIcon' ], this); - if ('onfullscreenchange' in window.document) { + if ('onfullscreenchange' in document) { this._fullscreenchange = 'fullscreenchange'; - } else if ('onwebkitfullscreenchange' in window.document) { + } else if ('onwebkitfullscreenchange' in document) { this._fullscreenchange = 'webkitfullscreenchange'; } } @@ -69,13 +68,13 @@ class FullscreenControl { this._controlContainer.remove(); this._map = (null: any); // $FlowFixMe[method-unbinding] - window.document.removeEventListener(this._fullscreenchange, this._changeIcon); + document.removeEventListener(this._fullscreenchange, this._changeIcon); } _checkFullscreenSupport(): boolean { return !!( - window.document.fullscreenEnabled || - (window.document: any).webkitFullscreenEnabled + document.fullscreenEnabled || + (document: any).webkitFullscreenEnabled ); } @@ -87,7 +86,7 @@ class FullscreenControl { // $FlowFixMe[method-unbinding] this._fullscreenButton.addEventListener('click', this._onClickFullscreen); // $FlowFixMe[method-unbinding] - window.document.addEventListener(this._fullscreenchange, this._changeIcon); + document.addEventListener(this._fullscreenchange, this._changeIcon); } _updateTitle() { @@ -106,8 +105,8 @@ class FullscreenControl { _changeIcon() { const fullscreenElement = - window.document.fullscreenElement || - (window.document: any).webkitFullscreenElement; + document.fullscreenElement || + (document: any).webkitFullscreenElement; if ((fullscreenElement === this._container) !== this._fullscreen) { this._fullscreen = !this._fullscreen; @@ -119,10 +118,12 @@ class FullscreenControl { _onClickFullscreen() { if (this._isFullscreen()) { - if (window.document.exitFullscreen) { - (window.document: any).exitFullscreen(); - } else if (window.document.webkitCancelFullScreen) { - (window.document: any).webkitCancelFullScreen(); + // $FlowFixMe[method-unbinding] + if (document.exitFullscreen) { + (document: any).exitFullscreen(); + // $FlowFixMe[method-unbinding] + } else if ((document: any).webkitCancelFullScreen) { + (document: any).webkitCancelFullScreen(); } // $FlowFixMe[method-unbinding] } else if (this._container.requestFullscreen) { diff --git a/src/ui/control/geolocate_control.js b/src/ui/control/geolocate_control.js index f9c63311942..71a2e9e66ec 100644 --- a/src/ui/control/geolocate_control.js +++ b/src/ui/control/geolocate_control.js @@ -2,7 +2,6 @@ import {Event, Evented} from '../../util/evented.js'; import * as DOM from '../../util/dom.js'; -import window from '../../util/window.js'; import {extend, bindAll, warnOnce} from '../../util/util.js'; import assert from 'assert'; import Marker from '../marker.js'; @@ -10,7 +9,7 @@ import LngLat from '../../geo/lng_lat.js'; import throttle from '../../util/throttle.js'; import {mercatorZfromAltitude} from '../../geo/mercator_coordinate.js'; -import type Map from '../map.js'; +import type {Map} from '../map.js'; import type {AnimationOptions, CameraOptions} from '../camera.js'; type Options = { @@ -55,12 +54,12 @@ const defaultOptions = { * Not all browsers support geolocation, * and some users may disable the feature. Geolocation support for modern * browsers including Chrome requires sites to be served over HTTPS. If - * geolocation support is not available, the GeolocateControl will show + * geolocation support is not available, the `GeolocateControl` will show * as disabled. * * The [zoom level](https://docs.mapbox.com/help/glossary/zoom-level/) applied depends on the accuracy of the geolocation provided by the device. * - * The GeolocateControl has two modes. If `trackUserLocation` is `false` (default) the control acts as a button, which when pressed will set the map's camera to target the user location. If the user moves, the map won't update. This is most suited for the desktop. If `trackUserLocation` is `true` the control acts as a toggle button that when active the user's location is actively monitored for changes. In this mode the GeolocateControl has three interaction states: + * The GeolocateControl has two modes. If `trackUserLocation` is `false` (default) the control acts as a button, which when pressed will set the map's camera to target the user location. If the user moves, the map won't update. This is most suited for the desktop. If `trackUserLocation` is `true` the control acts as a toggle button that when active the user's location is actively monitored for changes. In this mode the `GeolocateControl` has three interaction states: * * active - The map's camera automatically updates as the user's location changes, keeping the location dot in the center. This is the initial state, and the state upon clicking the `GeolocateControl` button. * * passive - The user's location dot automatically updates, but the map's camera does not. Occurs upon the user initiating a map movement. * * disabled - Occurs if geolocation is not available, disabled, or denied. @@ -71,7 +70,7 @@ const defaultOptions = { * @param {Object} [options] * @param {Object} [options.positionOptions={enableHighAccuracy: false, timeout: 6000}] A Geolocation API [PositionOptions](https://developer.mozilla.org/en-US/docs/Web/API/PositionOptions) object. * @param {Object} [options.fitBoundsOptions={maxZoom: 15}] A {@link Map#fitBounds} options object to use when the map is panned and zoomed to the user's location. The default is to use a `maxZoom` of 15 to limit how far the map will zoom in for very accurate locations. - * @param {Object} [options.trackUserLocation=false] If `true` the GeolocateControl becomes a toggle button and when active the map will receive updates to the user's location as it changes. + * @param {Object} [options.trackUserLocation=false] If `true` the `GeolocateControl` becomes a toggle button and when active the map will receive updates to the user's location as it changes. * @param {Object} [options.showAccuracyCircle=true] By default, if `showUserLocation` is `true`, a transparent circle will be drawn around the user location indicating the accuracy (95% confidence level) of the user's location. Set to `false` to disable. Always disabled when `showUserLocation` is `false`. * @param {Object} [options.showUserLocation=true] By default a dot will be shown on the map at the user's location. Set to `false` to disable. * @param {Object} [options.showUserHeading=false] If `true` an arrow will be drawn next to the user location dot indicating the device's heading. This only has affect when `trackUserLocation` is `true`. @@ -109,9 +108,9 @@ class GeolocateControl extends Evented { _noTimeout: boolean; _supportsGeolocation: boolean; - constructor(options: $Shape) { + constructor(options?: $Shape) { super(); - const geolocation = window.navigator.geolocation; + const geolocation = navigator.geolocation; this.options = extend({geolocation}, defaultOptions, options); bindAll([ @@ -171,11 +170,11 @@ class GeolocateControl extends Evented { if (this._supportsGeolocation !== undefined) { callback(this._supportsGeolocation); - } else if (window.navigator.permissions !== undefined) { + } else if (navigator.permissions !== undefined) { // navigator.permissions has incomplete browser support http://caniuse.com/#feat=permissions-api // Test for the case where a browser disables Geolocation because of an insecure origin; // in some environments like iOS16 WebView, permissions reject queries but still support geolocation - window.navigator.permissions.query({name: 'geolocation'}) + navigator.permissions.query({name: 'geolocation'}) .then(p => updateSupport(p.state !== 'denied')) .catch(() => updateSupport()); @@ -185,10 +184,10 @@ class GeolocateControl extends Evented { } /** - * Check if the Geolocation API Position is outside the map's maxbounds. + * Check if the Geolocation API Position is outside the map's `maxBounds`. * * @param {Position} position the Geolocation API Position - * @returns {boolean} Returns `true` if position is outside the map's maxbounds, otherwise returns `false`. + * @returns {boolean} Returns `true` if position is outside the map's `maxBounds`, otherwise returns `false`. * @private */ _isOutOfMapMaxBounds(position: Position): boolean { @@ -232,7 +231,7 @@ class GeolocateControl extends Evented { } /** - * When the Geolocation API returns a new location, update the GeolocateControl. + * When the Geolocation API returns a new location, update the `GeolocateControl`. * * @param {Position} position the Geolocation API Position * @private @@ -494,7 +493,7 @@ class GeolocateControl extends Evented { * Programmatically request and move the map to the user's location. * * @returns {boolean} Returns `false` if called before control was added to a map, otherwise returns `true`. - * Called on a deviceorientation event. + * Called on a `deviceorientation` event. * * @param deviceOrientationEvent {DeviceOrientationEvent} * @private @@ -663,8 +662,8 @@ class GeolocateControl extends Evented { } }; - if (typeof window.DeviceMotionEvent !== "undefined" && - typeof window.DeviceMotionEvent.requestPermission === 'function') { + // $FlowFixMe[cannot-resolve-name] + if (typeof DeviceMotionEvent !== "undefined" && typeof DeviceMotionEvent.requestPermission === 'function') { // $FlowFixMe DeviceOrientationEvent.requestPermission() .then(response => { @@ -788,7 +787,7 @@ export default GeolocateControl; */ /** - * Fired when the GeolocateControl changes to the active lock state, which happens either upon first obtaining a successful Geolocation API position for the user (a geolocate event will follow), or when the user clicks the geolocate button when in the background state, which uses the last known position to recenter the map and enter active lock state (no geolocate event will follow unless the users's location changes). + * Fired when the `GeolocateControl` changes to the active lock state, which happens either upon first obtaining a successful Geolocation API position for the user (a `geolocate` event will follow), or when the user clicks the geolocate button when in the background state, which uses the last known position to recenter the map and enter active lock state (no `geolocate` event will follow unless the users's location changes). * * @event trackuserlocationstart * @memberof GeolocateControl @@ -811,7 +810,7 @@ export default GeolocateControl; */ /** - * Fired when the GeolocateControl changes to the background state, which happens when a user changes the camera during an active position lock. This only applies when trackUserLocation is true. In the background state, the dot on the map will update with location updates but the camera will not. + * Fired when the `GeolocateControl` changes to the background state, which happens when a user changes the camera during an active position lock. This only applies when `trackUserLocation` is `true`. In the background state, the dot on the map will update with location updates but the camera will not. * * @event trackuserlocationend * @memberof GeolocateControl diff --git a/src/ui/control/logo_control.js b/src/ui/control/logo_control.js index 6ee16ee3724..e18d5a5f4ba 100644 --- a/src/ui/control/logo_control.js +++ b/src/ui/control/logo_control.js @@ -3,7 +3,7 @@ import * as DOM from '../../util/dom.js'; import {bindAll} from '../../util/util.js'; -import type Map, {ControlPosition} from '../map.js'; +import type {Map, ControlPosition} from '../map.js'; /** * A `LogoControl` is a control that adds the Mapbox watermark diff --git a/src/ui/control/navigation_control.js b/src/ui/control/navigation_control.js index b768fa4bbe7..e1308494e3e 100644 --- a/src/ui/control/navigation_control.js +++ b/src/ui/control/navigation_control.js @@ -3,9 +3,8 @@ import * as DOM from '../../util/dom.js'; import {extend, bindAll} from '../../util/util.js'; import {MouseRotateHandler, MousePitchHandler} from '../handler/mouse.js'; -import window from '../../util/window.js'; -import type Map from '../map.js'; +import type {Map} from '../map.js'; import type Point from '@mapbox/point-geometry'; type Options = { @@ -50,7 +49,7 @@ class NavigationControl { _compassIcon: HTMLElement; _handler: ?MouseRotateWrapper; - constructor(options: Options) { + constructor(options?: Options) { this.options = extend({}, defaultOptions, options); this._container = DOM.create('div', 'mapboxgl-ctrl mapboxgl-ctrl-group'); diff --git a/src/ui/control/scale_control.js b/src/ui/control/scale_control.js index 6b6d71ed6af..e7ac3ad87ae 100644 --- a/src/ui/control/scale_control.js +++ b/src/ui/control/scale_control.js @@ -3,7 +3,7 @@ import * as DOM from '../../util/dom.js'; import {extend, bindAll} from '../../util/util.js'; -import type Map, {ControlPosition} from '../map.js'; +import type {Map, ControlPosition} from '../map.js'; type Unit = 'imperial' | 'metric' | 'nautical'; @@ -49,7 +49,7 @@ class ScaleControl { _isNumberFormatSupported: boolean; options: Options; - constructor(options: Options) { + constructor(options?: Options) { this.options = extend({}, defaultOptions, options); // Some old browsers (e.g., Safari < 14.1) don't support the "unit" style in NumberFormat. diff --git a/src/ui/events.js b/src/ui/events.js index ab85e0c7126..dad7b1f1b7b 100644 --- a/src/ui/events.js +++ b/src/ui/events.js @@ -6,7 +6,7 @@ import * as DOM from '../util/dom.js'; import Point from '@mapbox/point-geometry'; import {extend} from '../util/util.js'; -import type Map from './map.js'; +import type {Map} from './map.js'; import type LngLat from '../geo/lng_lat.js'; /** @@ -409,11 +409,11 @@ export type MapBoxZoomEvent = { * @property {Object} [source] The [style spec representation of the source](https://docs.mapbox.com/mapbox-gl-js/style-spec/sources/) if the event has a `dataType` of `source`. * @property {string} [sourceId] The `id` of the [`source`](https://docs.mapbox.com/mapbox-gl-js/style-spec/sources/) that triggered the event, if the event has a `dataType` of `source`. Same as the `id` of the object in the `source` property. * @property {string} [sourceDataType] Included if the event has a `dataType` of `source` and the event signals - * that internal data has been received or changed. Possible values are `metadata`, `content` and `visibility`. + * that internal data has been received or changed. Possible values are `metadata`, `content` and `visibility`, and `error`. * @property {Object} [tile] The tile being loaded or changed, if the event has a `dataType` of `source` and - * the event is related to loading of a tile. + * the event is related to loading of a tile. * @property {Coordinate} [coord] The coordinate of the tile if the event has a `dataType` of `source` and - * the event is related to loading of a tile. + * the event is related to loading of a tile. * @example * // Example of a MapDataEvent of type "sourcedata" * map.on('sourcedata', (e) => { diff --git a/src/ui/free_camera.js b/src/ui/free_camera.js index 8afc02384b6..f57e3a566fd 100644 --- a/src/ui/free_camera.js +++ b/src/ui/free_camera.js @@ -206,7 +206,7 @@ class FreeCamera { return [col[0], col[1], col[2]]; } - set position(value: ?Vec3) { + set position(value: Vec3 | null | void) { if (value) { updateTransformPosition(this._transform, value); } @@ -216,7 +216,7 @@ class FreeCamera { return this._orientation; } - set orientation(value: ?Quat) { + set orientation(value: Quat | null | void) { this._orientation = value || quat.identity([]); if (value) { updateTransformOrientation(this._transform, this._orientation); diff --git a/src/ui/handler.js b/src/ui/handler.js new file mode 100644 index 00000000000..181a4c9c301 --- /dev/null +++ b/src/ui/handler.js @@ -0,0 +1,63 @@ +// @flow + +import type {Map} from './map.js'; +import type Point from '@mapbox/point-geometry'; +import type MercatorCoordinate from '../geo/mercator_coordinate.js'; + +// Handlers interpret dom events and return camera changes that should be +// applied to the map (`HandlerResult`s). The camera changes are all deltas. +// The handler itself should have no knowledge of the map's current state. +// This makes it easier to merge multiple results and keeps handlers simpler. +// For example, if there is a mousedown and mousemove, the mousePan handler +// would return a `panDelta` on the mousemove. +export interface Handler { + enable(): void; + disable(): void; + isEnabled(): boolean; + isActive(): boolean; + + // `reset` can be called by the manager at any time and must reset everything to it's original state + reset(): void; + + // Handlers can optionally implement these methods. + // They are called with dom events whenever those dom evens are received. + +touchstart?: (e: TouchEvent, points: Array, mapTouches: Array) => ?HandlerResult | void; + +touchmove?: (e: TouchEvent, points: Array, mapTouches: Array) => ?HandlerResult | void; + +touchend?: (e: TouchEvent, points: Array, mapTouches: Array) => ?HandlerResult | void; + +touchcancel?: (e: TouchEvent, points: Array, mapTouches: Array) => ?HandlerResult | void; + +mousedown?: (e: MouseEvent, point: Point) => ?HandlerResult | void; + +mousemove?: (e: MouseEvent, point: Point) => ?HandlerResult | void; + +mouseup?: (e: MouseEvent, point: Point) => ?HandlerResult | void; + +dblclick?: (e: MouseEvent, point: Point) => ?HandlerResult | void; + +wheel?: (e: WheelEvent, point: Point) => ?HandlerResult | void; + +keydown?: (e: KeyboardEvent) => ?HandlerResult | void; + +keyup?: (e: KeyboardEvent) => ?HandlerResult | void; + + // `renderFrame` is the only non-dom event. It is called during render + // frames and can be used to smooth camera changes (see scroll handler). + +renderFrame?: () => ?HandlerResult | void; +} + +// All handler methods that are called with events can optionally return a `HandlerResult`. +export type HandlerResult = { + panDelta?: Point, + zoomDelta?: number, + bearingDelta?: number, + pitchDelta?: number, + // the point to not move when changing the camera + around?: Point | null, + // same as above, except for pinch actions, which are given higher priority + pinchAround?: Point | null, + // the point to not move when changing the camera in mercator coordinates + aroundCoord?: MercatorCoordinate | null, + // A method that can fire a one-off easing by directly changing the map's camera. + cameraAnimation?: (map: Map) => any; + + // The last three properties are needed by only one handler: scrollzoom. + // The DOM event to be used as the `originalEvent` on any camera change events. + originalEvent?: any, + // Makes the manager trigger a frame, allowing the handler to return multiple results over time (see scrollzoom). + needsRenderFrame?: boolean, + // The camera changes won't get recorded for inertial zooming. + noInertia?: boolean +}; diff --git a/src/ui/handler/box_zoom.js b/src/ui/handler/box_zoom.js index 29386050569..4feb8d98efe 100644 --- a/src/ui/handler/box_zoom.js +++ b/src/ui/handler/box_zoom.js @@ -4,9 +4,9 @@ import * as DOM from '../../util/dom.js'; import {Event} from '../../util/evented.js'; -import type Map from '../map.js'; +import type {Map} from '../map.js'; import type Point from '@mapbox/point-geometry'; -import type {HandlerResult} from '../handler_manager.js'; +import type {Handler, HandlerResult} from '../handler.js'; /** * The `BoxZoomHandler` allows the user to zoom the map to fit within a bounding box. @@ -15,7 +15,7 @@ import type {HandlerResult} from '../handler_manager.js'; * @see [Example: Toggle interactions](https://docs.mapbox.com/mapbox-gl-js/example/toggle-interaction-handlers/) * @see [Example: Highlight features within a bounding box](https://docs.mapbox.com/mapbox-gl-js/example/using-box-queryrenderedfeatures/) */ -class BoxZoomHandler { +class BoxZoomHandler implements Handler { _map: Map; _el: HTMLElement; _container: HTMLElement; @@ -82,6 +82,7 @@ class BoxZoomHandler { this._enabled = false; } + // $FlowFixMe[method-unbinding] mousedown(e: MouseEvent, point: Point) { if (!this.isEnabled()) return; if (!(e.shiftKey && e.button === 0)) return; @@ -146,6 +147,7 @@ class BoxZoomHandler { } } + // $FlowFixMe[method-unbinding] keydown(e: KeyboardEvent) { if (!this._active) return; diff --git a/src/ui/handler/click_zoom.js b/src/ui/handler/click_zoom.js index 9010c5b4fbf..1a675e1e0b5 100644 --- a/src/ui/handler/click_zoom.js +++ b/src/ui/handler/click_zoom.js @@ -1,11 +1,10 @@ // @flow import type Point from '@mapbox/point-geometry'; -import type Map from '../map.js'; -import type {HandlerResult} from '../handler_manager.js'; - -export default class ClickZoomHandler { +import type {Map} from '../map.js'; +import type {Handler, HandlerResult} from '../handler.js'; +export default class ClickZoomHandler implements Handler { _enabled: boolean; _active: boolean; @@ -21,6 +20,7 @@ export default class ClickZoomHandler { this.reset(); } + // $FlowFixMe[method-unbinding] dblclick(e: MouseEvent, point: Point): HandlerResult { e.preventDefault(); return { diff --git a/src/ui/handler/keyboard.js b/src/ui/handler/keyboard.js index 491a893405d..81e5493e5d7 100644 --- a/src/ui/handler/keyboard.js +++ b/src/ui/handler/keyboard.js @@ -1,7 +1,7 @@ // @flow -import type Map from '../map.js'; -import type {HandlerResult} from '../handler_manager.js'; +import type {Map} from '../map.js'; +import type {Handler, HandlerResult} from '../handler.js'; const defaultOptions = { panStep: 100, @@ -27,7 +27,7 @@ const defaultOptions = { * @see [Example: Navigate the map with game-like controls](https://docs.mapbox.com/mapbox-gl-js/example/game-controls/) * @see [Example: Display map navigation controls](https://docs.mapbox.com/mapbox-gl-js/example/navigation/) */ -class KeyboardHandler { +class KeyboardHandler implements Handler { _enabled: boolean; _active: boolean; _panStep: number; @@ -54,6 +54,7 @@ class KeyboardHandler { this._active = false; } + // $FlowFixMe[method-unbinding] keydown(e: KeyboardEvent): ?HandlerResult { if (e.altKey || e.ctrlKey || e.metaKey) return; @@ -166,7 +167,7 @@ class KeyboardHandler { * interaction is enabled. * * @returns {boolean} `true` if the "keyboard rotate and zoom" - * interaction is enabled. + * interaction is enabled. * @example * const isKeyboardEnabled = map.keyboard.isEnabled(); */ @@ -179,7 +180,7 @@ class KeyboardHandler { * zoom/rotate gesture. * * @returns {boolean} `true` if the handler is enabled and has detected the - * start of a zoom/rotate gesture. + * start of a zoom/rotate gesture. * @example * const isKeyboardActive = map.keyboard.isActive(); */ diff --git a/src/ui/handler/map_event.js b/src/ui/handler/map_event.js index a611fa0cc92..f4c5474e330 100644 --- a/src/ui/handler/map_event.js +++ b/src/ui/handler/map_event.js @@ -3,12 +3,11 @@ import {extend} from '../../util/util.js'; import {MapMouseEvent, MapTouchEvent, MapWheelEvent} from '../events.js'; -import type Map from '../map.js'; +import type {Map} from '../map.js'; import type Point from '@mapbox/point-geometry'; -import type {HandlerResult} from '../handler_manager.js'; - -export class MapEventHandler { +import type {Handler, HandlerResult} from '../handler.js'; +export class MapEventHandler implements Handler { _mousedownPos: ?Point; _clickTolerance: number; _map: Map; @@ -22,12 +21,14 @@ export class MapEventHandler { this._mousedownPos = undefined; } + // $FlowFixMe[method-unbinding] wheel(e: WheelEvent): ?HandlerResult { // If mapEvent.preventDefault() is called by the user, prevent handlers such as: // - ScrollZoom return this._firePreventable(new MapWheelEvent(e.type, this._map, e)); } + // $FlowFixMe[method-unbinding] mousedown(e: MouseEvent, point: Point): ?HandlerResult { this._mousedownPos = point; // If mapEvent.preventDefault() is called by the user, prevent handlers such as: @@ -38,6 +39,7 @@ export class MapEventHandler { return this._firePreventable(new MapMouseEvent(e.type, this._map, e)); } + // $FlowFixMe[method-unbinding] mouseup(e: MouseEvent) { this._map.fire(new MapMouseEvent(e.type, this._map, e)); } @@ -54,6 +56,7 @@ export class MapEventHandler { this._map.fire(new MapMouseEvent(e.type, this._map, e)); } + // $FlowFixMe[method-unbinding] dblclick(e: MouseEvent): ?HandlerResult { // If mapEvent.preventDefault() is called by the user, prevent handlers such as: // - DblClickZoom @@ -68,6 +71,7 @@ export class MapEventHandler { this._map.fire(new MapMouseEvent(e.type, this._map, e)); } + // $FlowFixMe[method-unbinding] touchstart(e: TouchEvent): ?HandlerResult { // If mapEvent.preventDefault() is called by the user, prevent handlers such as: // - TouchPan @@ -79,14 +83,17 @@ export class MapEventHandler { return this._firePreventable(new MapTouchEvent(e.type, this._map, e)); } + // $FlowFixMe[method-unbinding] touchmove(e: TouchEvent) { this._map.fire(new MapTouchEvent(e.type, this._map, e)); } + // $FlowFixMe[method-unbinding] touchend(e: TouchEvent) { this._map.fire(new MapTouchEvent(e.type, this._map, e)); } + // $FlowFixMe[method-unbinding] touchcancel(e: TouchEvent) { this._map.fire(new MapTouchEvent(e.type, this._map, e)); } diff --git a/src/ui/handler/mouse.js b/src/ui/handler/mouse.js index df64d23075a..7a84e8a08b0 100644 --- a/src/ui/handler/mouse.js +++ b/src/ui/handler/mouse.js @@ -2,7 +2,7 @@ import * as DOM from '../../util/dom.js'; import type Point from '@mapbox/point-geometry'; -import type {HandlerResult} from '../handler_manager.js'; +import type {Handler, HandlerResult} from '../handler.js'; const LEFT_BUTTON = 0; const RIGHT_BUTTON = 2; @@ -18,8 +18,7 @@ function buttonStillPressed(e: MouseEvent, button: number) { return e.buttons === undefined || (e.buttons & flag) !== flag; } -class MouseHandler { - +class MouseHandler implements Handler { _enabled: boolean; _active: boolean; _lastPoint: ?Point; @@ -51,6 +50,7 @@ class MouseHandler { return {}; // implemented by child } + // $FlowFixMe[method-unbinding] mousedown(e: MouseEvent, point: Point) { if (this._lastPoint) return; diff --git a/src/ui/handler/scroll_zoom.js b/src/ui/handler/scroll_zoom.js index 5b444668052..3ab8e30045c 100644 --- a/src/ui/handler/scroll_zoom.js +++ b/src/ui/handler/scroll_zoom.js @@ -5,12 +5,12 @@ import * as DOM from '../../util/dom.js'; import {ease as _ease, bindAll, bezier, isFullscreen} from '../../util/util.js'; import browser from '../../util/browser.js'; -import window from '../../util/window.js'; import {number as interpolate} from '../../style-spec/util/interpolate.js'; import Point from '@mapbox/point-geometry'; -import type Map from '../map.js'; -import type HandlerManager, {HandlerResult} from '../handler_manager.js'; +import type {Map} from '../map.js'; +import type HandlerManager from '../handler_manager.js'; +import type {Handler, HandlerResult} from '../handler.js'; import MercatorCoordinate from '../../geo/mercator_coordinate.js'; // deltaY value for mouse scroll wheel identification @@ -31,7 +31,7 @@ const maxScalePerFrame = 2; * @see [Example: Toggle interactions](https://docs.mapbox.com/mapbox-gl-js/example/toggle-interaction-handlers/) * @see [Example: Disable scroll zoom](https://docs.mapbox.com/mapbox-gl-js/example/disable-scroll-zoom/) */ -class ScrollZoomHandler { +class ScrollZoomHandler implements Handler { _map: Map; _el: HTMLElement; _enabled: boolean; @@ -163,6 +163,7 @@ class ScrollZoomHandler { } } + // $FlowFixMe[method-unbinding] wheel(e: WheelEvent) { if (!this.isEnabled()) return; @@ -178,7 +179,7 @@ class ScrollZoomHandler { } // Remove `any` cast when https://github.com/facebook/flow/issues/4879 is fixed. - let value = e.deltaMode === (window.WheelEvent: any).DOM_DELTA_LINE ? e.deltaY * 40 : e.deltaY; + let value = e.deltaMode === (WheelEvent: any).DOM_DELTA_LINE ? e.deltaY * 40 : e.deltaY; const now = browser.now(), timeDelta = now - (this._lastWheelEventTime || 0); @@ -266,6 +267,7 @@ class ScrollZoomHandler { } } + // $FlowFixMe[method-unbinding] renderFrame(): ?HandlerResult { if (!this._frameId) return; this._frameId = null; @@ -402,7 +404,7 @@ class ScrollZoomHandler { if (this._map && !this._alertContainer) { this._alertContainer = DOM.create('div', 'mapboxgl-scroll-zoom-blocker', this._map._container); - if (/(Mac|iPad)/i.test(window.navigator.userAgent)) { + if (/(Mac|iPad)/i.test(navigator.userAgent)) { this._alertContainer.textContent = this._map._getUIString('ScrollZoomBlocker.CmdMessage'); } else { this._alertContainer.textContent = this._map._getUIString('ScrollZoomBlocker.CtrlMessage'); @@ -422,7 +424,7 @@ class ScrollZoomHandler { this._alertTimer = setTimeout(() => { this._alertContainer.classList.remove('mapboxgl-scroll-zoom-blocker-show'); - this._alertContainer.setAttribute("role", "null"); + this._alertContainer.removeAttribute("role"); }, 200); } diff --git a/src/ui/handler/tap_drag_zoom.js b/src/ui/handler/tap_drag_zoom.js index 0953fd5d6c4..fa4a4a609ab 100644 --- a/src/ui/handler/tap_drag_zoom.js +++ b/src/ui/handler/tap_drag_zoom.js @@ -2,10 +2,9 @@ import {TapRecognizer, MAX_TAP_INTERVAL} from './tap_recognizer.js'; import type Point from '@mapbox/point-geometry'; -import type {HandlerResult} from '../handler_manager.js'; - -export default class TapDragZoomHandler { +import type {Handler, HandlerResult} from '../handler.js'; +export default class TapDragZoomHandler implements Handler { _enabled: boolean; _active: boolean; _swipePoint: ?Point; @@ -31,6 +30,7 @@ export default class TapDragZoomHandler { this._tap.reset(); } + // $FlowFixMe[method-unbinding] touchstart(e: TouchEvent, points: Array, mapTouches: Array) { if (this._swipePoint) return; @@ -47,6 +47,7 @@ export default class TapDragZoomHandler { } + // $FlowFixMe[method-unbinding] touchmove(e: TouchEvent, points: Array, mapTouches: Array): ?HandlerResult { if (!this._tapTime) { this._tap.touchmove(e, points, mapTouches); @@ -68,6 +69,7 @@ export default class TapDragZoomHandler { } } + // $FlowFixMe[method-unbinding] touchend(e: TouchEvent, points: Array, mapTouches: Array) { if (!this._tapTime) { const point = this._tap.touchend(e, points, mapTouches); @@ -81,6 +83,7 @@ export default class TapDragZoomHandler { } } + // $FlowFixMe[method-unbinding] touchcancel() { this.reset(); } diff --git a/src/ui/handler/tap_zoom.js b/src/ui/handler/tap_zoom.js index afeb7a48760..21ee2b53f44 100644 --- a/src/ui/handler/tap_zoom.js +++ b/src/ui/handler/tap_zoom.js @@ -2,11 +2,10 @@ import {TapRecognizer} from './tap_recognizer.js'; import type Point from '@mapbox/point-geometry'; -import type Map from '../map.js'; -import type {HandlerResult} from '../handler_manager.js'; - -export default class TapZoomHandler { +import type {Map} from '../map.js'; +import type {Handler, HandlerResult} from '../handler.js'; +export default class TapZoomHandler implements Handler { _enabled: boolean; _active: boolean; _zoomIn: TapRecognizer; @@ -32,16 +31,19 @@ export default class TapZoomHandler { this._zoomOut.reset(); } + // $FlowFixMe[method-unbinding] touchstart(e: TouchEvent, points: Array, mapTouches: Array) { this._zoomIn.touchstart(e, points, mapTouches); this._zoomOut.touchstart(e, points, mapTouches); } + // $FlowFixMe[method-unbinding] touchmove(e: TouchEvent, points: Array, mapTouches: Array) { this._zoomIn.touchmove(e, points, mapTouches); this._zoomOut.touchmove(e, points, mapTouches); } + // $FlowFixMe[method-unbinding] touchend(e: TouchEvent, points: Array, mapTouches: Array): ?HandlerResult { const zoomInPoint = this._zoomIn.touchend(e, points, mapTouches); const zoomOutPoint = this._zoomOut.touchend(e, points, mapTouches); @@ -71,6 +73,7 @@ export default class TapZoomHandler { } } + // $FlowFixMe[method-unbinding] touchcancel() { this.reset(); } diff --git a/src/ui/handler/touch_pan.js b/src/ui/handler/touch_pan.js index 62a6d5a80d2..428c5a5a2fd 100644 --- a/src/ui/handler/touch_pan.js +++ b/src/ui/handler/touch_pan.js @@ -1,14 +1,13 @@ // @flow import Point from '@mapbox/point-geometry'; -import type Map from '../map.js'; +import type {Map} from '../map.js'; import {indexTouches} from './handler_util.js'; import {bindAll, isFullscreen} from '../../util/util.js'; import * as DOM from '../../util/dom.js'; -import type {HandlerResult} from '../handler_manager.js'; - -export default class TouchPanHandler { +import type {Handler, HandlerResult} from '../handler.js'; +export default class TouchPanHandler implements Handler { _map: Map; _el: HTMLElement; _enabled: boolean; @@ -35,10 +34,12 @@ export default class TouchPanHandler { this._sum = new Point(0, 0); } + // $FlowFixMe[method-unbinding] touchstart(e: TouchEvent, points: Array, mapTouches: Array): ?HandlerResult { return this._calculateTransform(e, points, mapTouches); } + // $FlowFixMe[method-unbinding] touchmove(e: TouchEvent, points: Array, mapTouches: Array): ?HandlerResult { if (!this._active || mapTouches.length < this._minTouches) return; @@ -61,6 +62,7 @@ export default class TouchPanHandler { return this._calculateTransform(e, points, mapTouches); } + // $FlowFixMe[method-unbinding] touchend(e: TouchEvent, points: Array, mapTouches: Array) { this._calculateTransform(e, points, mapTouches); @@ -69,6 +71,7 @@ export default class TouchPanHandler { } } + // $FlowFixMe[method-unbinding] touchcancel() { this.reset(); } @@ -156,7 +159,7 @@ export default class TouchPanHandler { this._alertTimer = setTimeout(() => { this._alertContainer.classList.remove('mapboxgl-touch-pan-blocker-show'); - this._alertContainer.setAttribute("role", "null"); + this._alertContainer.removeAttribute("role"); }, 500); } diff --git a/src/ui/handler/touch_zoom_rotate.js b/src/ui/handler/touch_zoom_rotate.js index 224c5383d37..a387b484532 100644 --- a/src/ui/handler/touch_zoom_rotate.js +++ b/src/ui/handler/touch_zoom_rotate.js @@ -2,12 +2,11 @@ import Point from '@mapbox/point-geometry'; import * as DOM from '../../util/dom.js'; -import type Map from '../map.js'; -import type {HandlerResult} from '../handler_manager.js'; +import type {Map} from '../map.js'; +import type {Handler, HandlerResult} from '../handler.js'; import {isFullscreen} from '../../util/util.js'; -class TwoTouchHandler { - +class TwoTouchHandler implements Handler { _enabled: boolean; _active: boolean; _firstTwoTouches: ?[number, number]; @@ -27,6 +26,7 @@ class TwoTouchHandler { _start(points: [Point, Point]) {} //eslint-disable-line _move(points: [Point, Point], pinchAround: ?Point, e: TouchEvent): ?HandlerResult { return {}; } //eslint-disable-line + // $FlowFixMe[method-unbinding] touchstart(e: TouchEvent, points: Array, mapTouches: Array) { //console.log(e.target, e.targetTouches.length ? e.targetTouches[0].target : null); //log('touchstart', points, e.target.innerHTML, e.targetTouches.length ? e.targetTouches[0].target.innerHTML: undefined); @@ -41,6 +41,7 @@ class TwoTouchHandler { this._start([points[0], points[1]]); } + // $FlowFixMe[method-unbinding] touchmove(e: TouchEvent, points: Array, mapTouches: Array): ?HandlerResult { const firstTouches = this._firstTwoTouches; if (!firstTouches) return; @@ -58,6 +59,7 @@ class TwoTouchHandler { } + // $FlowFixMe[method-unbinding] touchend(e: TouchEvent, points: Array, mapTouches: Array) { if (!this._firstTwoTouches) return; @@ -71,6 +73,7 @@ class TwoTouchHandler { this.reset(); } + // $FlowFixMe[method-unbinding] touchcancel() { this.reset(); } diff --git a/src/ui/handler_inertia.js b/src/ui/handler_inertia.js index 0369562a022..a3a2a477b93 100644 --- a/src/ui/handler_inertia.js +++ b/src/ui/handler_inertia.js @@ -1,7 +1,7 @@ // @flow import browser from '../util/browser.js'; -import type Map from './map.js'; +import type {Map} from './map.js'; import {bezier, clamp, extend} from '../util/util.js'; import Point from '@mapbox/point-geometry'; diff --git a/src/ui/handler_manager.js b/src/ui/handler_manager.js index 8798230ba77..cd5e3fef867 100644 --- a/src/ui/handler_manager.js +++ b/src/ui/handler_manager.js @@ -2,7 +2,6 @@ import {Event} from '../util/evented.js'; import * as DOM from '../util/dom.js'; -import type Map from './map.js'; import HandlerInertia from './handler_inertia.js'; import {MapEventHandler, BlockableMapEventHandler} from './handler/map_event.js'; import BoxZoomHandler from './handler/box_zoom.js'; @@ -19,13 +18,14 @@ import DragPanHandler from './handler/shim/drag_pan.js'; import DragRotateHandler from './handler/shim/drag_rotate.js'; import TouchZoomRotateHandler from './handler/shim/touch_zoom_rotate.js'; import {bindAll, extend} from '../util/util.js'; -import window from '../util/window.js'; import Point from '@mapbox/point-geometry'; import assert from 'assert'; import {vec3} from 'gl-matrix'; import MercatorCoordinate, {latFromMercatorY, mercatorScale} from '../geo/mercator_coordinate.js'; +import type {Map} from './map.js'; import type {Vec3} from 'gl-matrix'; +import type {Handler, HandlerResult} from './handler.js'; export type InputEvent = MouseEvent | TouchEvent | KeyboardEvent | WheelEvent; @@ -81,64 +81,6 @@ class TrackingEllipsoid { } } -// Handlers interpret dom events and return camera changes that should be -// applied to the map (`HandlerResult`s). The camera changes are all deltas. -// The handler itself should have no knowledge of the map's current state. -// This makes it easier to merge multiple results and keeps handlers simpler. -// For example, if there is a mousedown and mousemove, the mousePan handler -// would return a `panDelta` on the mousemove. -export interface Handler { - enable(): void; - disable(): void; - isEnabled(): boolean; - isActive(): boolean; - - // `reset` can be called by the manager at any time and must reset everything to it's original state - reset(): void; - - // Handlers can optionally implement these methods. - // They are called with dom events whenever those dom evens are received. - +touchstart?: (e: TouchEvent, points: Array, mapTouches: Array) => ?HandlerResult; - +touchmove?: (e: TouchEvent, points: Array, mapTouches: Array) => ?HandlerResult; - +touchend?: (e: TouchEvent, points: Array, mapTouches: Array) => ?HandlerResult; - +touchcancel?: (e: TouchEvent, points: Array, mapTouches: Array) => ?HandlerResult; - +mousedown?: (e: MouseEvent, point: Point) => ?HandlerResult; - +mousemove?: (e: MouseEvent, point: Point) => ?HandlerResult; - +mouseup?: (e: MouseEvent, point: Point) => ?HandlerResult; - +dblclick?: (e: MouseEvent, point: Point) => ?HandlerResult; - +wheel?: (e: WheelEvent, point: Point) => ?HandlerResult; - +keydown?: (e: KeyboardEvent) => ?HandlerResult; - +keyup?: (e: KeyboardEvent) => ?HandlerResult; - - // `renderFrame` is the only non-dom event. It is called during render - // frames and can be used to smooth camera changes (see scroll handler). - +renderFrame?: () => ?HandlerResult; -} - -// All handler methods that are called with events can optionally return a `HandlerResult`. -export type HandlerResult = { - panDelta?: Point, - zoomDelta?: number, - bearingDelta?: number, - pitchDelta?: number, - // the point to not move when changing the camera - around?: Point | null, - // same as above, except for pinch actions, which are given higher priority - pinchAround?: Point | null, - // the point to not move when changing the camera in mercator coordinates - aroundCoord?: MercatorCoordinate | null, - // A method that can fire a one-off easing by directly changing the map's camera. - cameraAnimation?: (map: Map) => any; - - // The last three properties are needed by only one handler: scrollzoom. - // The DOM event to be used as the `originalEvent` on any camera change events. - originalEvent?: any, - // Makes the manager trigger a frame, allowing the handler to return multiple results over time (see scrollzoom). - needsRenderFrame?: boolean, - // The camera changes won't get recorded for inertial zooming. - noInertia?: boolean -}; - function hasChange(result: HandlerResult) { return (result.panDelta && result.panDelta.mag()) || result.zoomDelta || result.bearingDelta || result.pitchDelta; } @@ -155,7 +97,7 @@ class HandlerManager { _updatingCamera: boolean; _changes: Array<[HandlerResult, Object, any]>; _previousActiveHandlers: { [string]: Handler }; - _listeners: Array<[HTMLElement, string, void | EventListenerOptionsOrUseCapture]>; + _listeners: Array<[HTMLElement | Document, string, void | EventListenerOptionsOrUseCapture]>; _trackingEllipsoid: TrackingEllipsoid; _dragOrigin: ?Vec3; _originalZoom: ?number; @@ -204,8 +146,8 @@ class HandlerManager { // window-level event listeners give us the best shot at capturing events that // fall outside the map canvas element. Use `{capture: true}` for the move event // to prevent map move events from being fired during a drag. - [window.document, 'mousemove', {capture: true}], - [window.document, 'mouseup', undefined], + [document, 'mousemove', {capture: true}], + [document, 'mouseup', undefined], [el, 'mouseover', undefined], [el, 'mouseout', undefined], @@ -223,7 +165,7 @@ class HandlerManager { for (const [target, type, listenerOptions] of this._listeners) { // $FlowFixMe[method-unbinding] - const listener = target === window.document ? this.handleWindowEvent : this.handleEvent; + const listener = target === document ? this.handleWindowEvent : this.handleEvent; target.addEventListener((type: any), (listener: any), listenerOptions); } } @@ -231,7 +173,7 @@ class HandlerManager { destroy() { for (const [target, type, listenerOptions] of this._listeners) { // $FlowFixMe[method-unbinding] - const listener = target === window.document ? this.handleWindowEvent : this.handleEvent; + const listener = target === document ? this.handleWindowEvent : this.handleEvent; target.removeEventListener((type: any), (listener: any), listenerOptions); } } diff --git a/src/ui/hash.js b/src/ui/hash.js index 4d1829a5639..6d7576090ab 100644 --- a/src/ui/hash.js +++ b/src/ui/hash.js @@ -1,10 +1,9 @@ // @flow import {bindAll} from '../util/util.js'; -import window from '../util/window.js'; import throttle from '../util/throttle.js'; -import type Map from './map.js'; +import type {Map} from './map.js'; /* * Adds the map's position to its page's location hash. @@ -70,7 +69,7 @@ export default class Hash { if (this._hashName) { const hashName = this._hashName; let found = false; - const parts = window.location.hash.slice(1).split('&').map(part => { + const parts = location.hash.slice(1).split('&').map(part => { const key = part.split('=')[0]; if (key === hashName) { found = true; @@ -89,7 +88,7 @@ export default class Hash { _getCurrentHash(): Array { // Get the current hash from location, stripped from its number sign - const hash = window.location.hash.replace('#', ''); + const hash = location.hash.replace('#', ''); if (this._hashName) { // Split the parameter-styled hash into parts and find the value we need let keyval; @@ -124,8 +123,7 @@ export default class Hash { _updateHashUnthrottled() { // Replace if already present, else append the updated hash string - const location = window.location.href.replace(/(#.+)?$/, this.getHashString()); - window.history.replaceState(window.history.state, null, location); + history.replaceState(history.state, '', location.href.replace(/(#.+)?$/, this.getHashString())); } } diff --git a/src/ui/map.js b/src/ui/map.js index d819356a50d..f0b3a3e1344 100755 --- a/src/ui/map.js +++ b/src/ui/map.js @@ -3,10 +3,20 @@ import {version} from '../../package.json'; import {asyncAll, extend, bindAll, warnOnce, uniqueId, isSafariWithAntialiasingBug} from '../util/util.js'; import browser from '../util/browser.js'; -import window from '../util/window.js'; import * as DOM from '../util/dom.js'; -import {getImage, getJSON, ResourceType} from '../util/ajax.js'; -import {RequestManager, getMapSessionAPI, postPerformanceEvent, postMapLoadEvent, AUTH_ERR_MSG, storeAuthState, removeAuthState} from '../util/mapbox.js'; +import {getImage, ResourceType} from '../util/ajax.js'; +import { + RequestManager, + mapSessionAPI, + mapLoadEvent, + getMapSessionAPI, + postPerformanceEvent, + postMapLoadEvent, + postStyleLoadEvent, + AUTH_ERR_MSG, + storeAuthState, + removeAuthState +} from '../util/mapbox.js'; import Style from '../style/style.js'; import EvaluationParameters from '../style/evaluation_parameters.js'; import Painter from '../render/painter.js'; @@ -14,8 +24,7 @@ import Transform from '../geo/transform.js'; import Hash from './hash.js'; import HandlerManager from './handler_manager.js'; import Camera from './camera.js'; -import LngLat from '../geo/lng_lat.js'; -import LngLatBounds from '../geo/lng_lat_bounds.js'; +import LngLat, {LngLatBounds} from '../geo/lng_lat.js'; import Point from '@mapbox/point-geometry'; import AttributionControl from './control/attribution_control.js'; import LogoControl from './control/logo_control.js'; @@ -31,16 +40,17 @@ import Marker from '../ui/marker.js'; import Popup from '../ui/popup.js'; import EasedVariable from '../util/eased_variable.js'; import SourceCache from '../source/source_cache.js'; -import {GLOBE_ZOOM_THRESHOLD_MAX} from '../geo/projection/globe_util.js'; +import {GLOBE_ZOOM_THRESHOLD_MAX} from '../geo/projection/globe_constants.js'; import {setCacheLimits} from '../util/tile_request_cache.js'; import {Debug} from '../util/debug.js'; import config from '../util/config.js'; import {isFQID} from '../util/fqid.js'; +import * as TP from '../tracked-parameters/tracked_parameters.js'; -import type {PointLike} from '@mapbox/point-geometry'; +import type {Listener} from '../util/evented.js'; +import type {PointLike} from '../types/point-like.js'; import type {RequestTransformFunction} from '../util/mapbox.js'; -import type {LngLatLike} from '../geo/lng_lat.js'; -import type {LngLatBoundsLike} from '../geo/lng_lat_bounds.js'; +import type {LngLatLike, LngLatBoundsLike} from '../geo/lng_lat.js'; import type {StyleOptions, StyleSetterOptions} from '../style/style.js'; import type {MapEvent, MapDataEvent} from './events.js'; import type {CustomLayerInterface} from '../style/style_layer/custom_style_layer.js'; @@ -50,7 +60,8 @@ import type ScrollZoomHandler from './handler/scroll_zoom.js'; import type BoxZoomHandler from './handler/box_zoom.js'; import type {TouchPitchHandler} from './handler/touch_zoom_rotate.js'; import type DragRotateHandler from './handler/shim/drag_rotate.js'; -import type DragPanHandler, {DragPanOptions} from './handler/shim/drag_pan.js'; +import type DragPanHandler from './handler/shim/drag_pan.js'; +import type {DragPanOptions} from './handler/shim/drag_pan.js'; import type KeyboardHandler from './handler/keyboard.js'; import type DoubleClickZoomHandler from './handler/shim/dblclick_zoom.js'; import type TouchZoomRotateHandler from './handler/shim/touch_zoom_rotate.js'; @@ -70,17 +81,21 @@ import type { ProjectionSpecification, PropertyValueSpecification, TransitionSpecification, - CameraSpecification + CameraSpecification, + ImportSpecification } from '../style-spec/types.js'; import type StyleLayer from '../style/style_layer.js'; import type {Source} from '../source/source.js'; import type {QueryFeature} from '../util/vectortile_to_geojson.js'; import type {QueryResult} from '../data/feature_index.js'; import type {EasingOptions} from './camera.js'; +import type {ContextOptions} from '../gl/context.js'; + +import type {ITrackedParameters} from 'tracked_parameters_proxy'; export type ControlPosition = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'; /* eslint-disable no-use-before-define */ -interface IControl { +export interface IControl { +onAdd: (map: Map) => HTMLElement; +onRemove: (map: Map) => void; @@ -94,8 +109,8 @@ export const AVERAGE_ELEVATION_EASE_TIME = 300; // ms export const AVERAGE_ELEVATION_EASE_THRESHOLD = 1; // meters export const AVERAGE_ELEVATION_CHANGE_THRESHOLD = 1e-4; // meters -type MapOptions = { - style: StyleSpecification | string | void, +export type MapOptions = { + style?: StyleSpecification | string | void, hash?: boolean | string, interactive?: boolean, container: HTMLElement | string, @@ -135,14 +150,17 @@ type MapOptions = { minTileCacheSize?: number, maxTileCacheSize?: number, transformRequest?: RequestTransformFunction, - accessToken: string, - testMode: ?boolean, + accessToken?: string, + testMode?: ?boolean, locale?: Object, language?: string, worldview?: string, crossSourceCollisions?: boolean, collectResourceTiming?: boolean, respectPrefersReducedMotion?: boolean, + contextCreateOptions?: ContextOptions, + devtools?: boolean, + repaint?: boolean }; const defaultMinZoom = -2; @@ -219,35 +237,35 @@ const defaultOptions = { * @param {number} [options.minPitch=0] The minimum pitch of the map (0-85). * @param {number} [options.maxPitch=85] The maximum pitch of the map (0-85). * @param {Object | string} [options.style='mapbox://styles/mapbox/standard'] The map's Mapbox style. This must be an a JSON object conforming to - * the schema described in the [Mapbox Style Specification](https://mapbox.com/mapbox-gl-style-spec/), or a URL - * to such JSON. Can accept a null value to allow adding a style manually. + * the schema described in the [Mapbox Style Specification](https://mapbox.com/mapbox-gl-style-spec/), or a URL + * to such JSON. Can accept a null value to allow adding a style manually. * - * To load a style from the Mapbox API, you can use a URL of the form `mapbox://styles/:owner/:style`, - * where `:owner` is your Mapbox account name and `:style` is the style ID. You can also use a - * [Mapbox-owned style](https://docs.mapbox.com/api/maps/styles/#mapbox-styles): + * To load a style from the Mapbox API, you can use a URL of the form `mapbox://styles/:owner/:style`, + * where `:owner` is your Mapbox account name and `:style` is the style ID. You can also use a + * [Mapbox-owned style](https://docs.mapbox.com/api/maps/styles/#mapbox-styles): * - * * `mapbox://styles/mapbox/standard` - * * `mapbox://styles/mapbox/streets-v12` - * * `mapbox://styles/mapbox/outdoors-v12` - * * `mapbox://styles/mapbox/light-v11` - * * `mapbox://styles/mapbox/dark-v11` - * * `mapbox://styles/mapbox/satellite-v9` - * * `mapbox://styles/mapbox/satellite-streets-v12` - * * `mapbox://styles/mapbox/navigation-day-v1` - * * `mapbox://styles/mapbox/navigation-night-v1`. + * * `mapbox://styles/mapbox/standard` + * * `mapbox://styles/mapbox/streets-v12` + * * `mapbox://styles/mapbox/outdoors-v12` + * * `mapbox://styles/mapbox/light-v11` + * * `mapbox://styles/mapbox/dark-v11` + * * `mapbox://styles/mapbox/satellite-v9` + * * `mapbox://styles/mapbox/satellite-streets-v12` + * * `mapbox://styles/mapbox/navigation-day-v1` + * * `mapbox://styles/mapbox/navigation-night-v1`. * - * Tilesets hosted with Mapbox can be style-optimized if you append `?optimize=true` to the end of your style URL, like `mapbox://styles/mapbox/streets-v11?optimize=true`. - * Learn more about style-optimized vector tiles in our [API documentation](https://www.mapbox.com/api-documentation/maps/#retrieve-tiles). + * Tilesets hosted with Mapbox can be style-optimized if you append `?optimize=true` to the end of your style URL, like `mapbox://styles/mapbox/streets-v11?optimize=true`. + * Learn more about style-optimized vector tiles in our [API documentation](https://www.mapbox.com/api-documentation/maps/#retrieve-tiles). * * @param {(boolean|string)} [options.hash=false] If `true`, the map's [position](https://docs.mapbox.com/help/glossary/camera) (zoom, center latitude, center longitude, bearing, and pitch) will be synced with the hash fragment of the page's URL. - * For example, `http://path/to/my/page.html#2.59/39.26/53.07/-24.1/60`. - * An additional string may optionally be provided to indicate a parameter-styled hash, - * for example http://path/to/my/page.html#map=2.59/39.26/53.07/-24.1/60&foo=bar, where `foo` - * is a custom parameter and `bar` is an arbitrary hash distinct from the map hash. + * For example, `http://path/to/my/page.html#2.59/39.26/53.07/-24.1/60`. + * An additional string may optionally be provided to indicate a parameter-styled hash, + * for example http://path/to/my/page.html#map=2.59/39.26/53.07/-24.1/60&foo=bar, where `foo` + * is a custom parameter and `bar` is an arbitrary hash distinct from the map hash. * @param {boolean} [options.interactive=true] If `false`, no mouse, touch, or keyboard listeners will be attached to the map, so it will not respond to interaction. * @param {number} [options.bearingSnap=7] The threshold, measured in degrees, that determines when the map's - * bearing will snap to north. For example, with a `bearingSnap` of 7, if the user rotates - * the map within 7 degrees of north, the map will automatically snap to exact north. + * bearing will snap to north. For example, with a `bearingSnap` of 7, if the user rotates + * the map within 7 degrees of north, the map will automatically snap to exact north. * @param {boolean} [options.pitchWithRotate=true] If `false`, the map's pitch (tilt) control with "drag to rotate" interaction will be disabled. * @param {number} [options.clickTolerance=3] The max number of pixels a user can shift the mouse pointer during a click for it to be considered a valid click (as opposed to a mouse drag). * @param {boolean} [options.attributionControl=true] If `true`, an {@link AttributionControl} will be added to the map. @@ -276,50 +294,50 @@ const defaultOptions = { * @param {LngLatBoundsLike} [options.bounds=null] The initial bounds of the map. If `bounds` is specified, it overrides `center` and `zoom` constructor options. * @param {Object} [options.fitBoundsOptions=null] A {@link Map#fitBounds} options object to use _only_ when fitting the initial `bounds` provided above. * @param {'auto' | string | string[]} [options.language=null] A string with a BCP 47 language tag, or an array of such strings representing the desired languages used for the map's labels and UI components. Languages can only be set on Mapbox vector tile sources. - * By default, GL JS will not set a language so that the language of Mapbox tiles will be determined by the vector tile source's TileJSON. - * Valid language strings must be a [BCP-47 language code](https://en.wikipedia.org/wiki/IETF_language_tag#List_of_subtags). Unsupported BCP-47 codes will not include any translations. Invalid codes will result in an recoverable error. - * If a label has no translation for the selected language, it will display in the label's local language. - * If option is set to `auto`, GL JS will select a user's preferred language as determined by the browser's [`window.navigator.language`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/language) property. - * If the `locale` property is not set separately, this language will also be used to localize the UI for supported languages. + * By default, GL JS will not set a language so that the language of Mapbox tiles will be determined by the vector tile source's TileJSON. + * Valid language strings must be a [BCP-47 language code](https://en.wikipedia.org/wiki/IETF_language_tag#List_of_subtags). Unsupported BCP-47 codes will not include any translations. Invalid codes will result in an recoverable error. + * If a label has no translation for the selected language, it will display in the label's local language. + * If option is set to `auto`, GL JS will select a user's preferred language as determined by the browser's [`window.navigator.language`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/language) property. + * If the `locale` property is not set separately, this language will also be used to localize the UI for supported languages. * @param {string} [options.worldview=null] Sets the map's worldview. A worldview determines the way that certain disputed boundaries - * are rendered. By default, GL JS will not set a worldview so that the worldview of Mapbox tiles will be determined by the vector tile source's TileJSON. - * Valid worldview strings must be an [ISO alpha-2 country code](https://en.wikipedia.org/wiki/ISO_3166-1#Current_codes). Unsupported - * ISO alpha-2 codes will fall back to the TileJSON's default worldview. Invalid codes will result in a recoverable error. + * are rendered. By default, GL JS will not set a worldview so that the worldview of Mapbox tiles will be determined by the vector tile source's TileJSON. + * Valid worldview strings must be an [ISO alpha-2 country code](https://en.wikipedia.org/wiki/ISO_3166-1#Current_codes). Unsupported + * ISO alpha-2 codes will fall back to the TileJSON's default worldview. Invalid codes will result in a recoverable error. * @param {boolean} [options.renderWorldCopies=true] If `true`, multiple copies of the world will be rendered side by side beyond -180 and 180 degrees longitude. If set to `false`: - * - When the map is zoomed out far enough that a single representation of the world does not fill the map's entire - * container, there will be blank space beyond 180 and -180 degrees longitude. - * - Features that cross 180 and -180 degrees longitude will be cut in two (with one portion on the right edge of the - * map and the other on the left edge of the map) at every zoom level. + * - When the map is zoomed out far enough that a single representation of the world does not fill the map's entire + * container, there will be blank space beyond 180 and -180 degrees longitude. + * - Features that cross 180 and -180 degrees longitude will be cut in two (with one portion on the right edge of the + * map and the other on the left edge of the map) at every zoom level. * @param {number} [options.minTileCacheSize=null] The minimum number of tiles stored in the tile cache for a given source. Larger viewports use more tiles and need larger caches. Larger viewports are more likely to be found on devices with more memory and on pages where the map is more important. If omitted, the cache will be dynamically sized based on the current viewport. * @param {number} [options.maxTileCacheSize=null] The maximum number of tiles stored in the tile cache for a given source. If omitted, the cache will be dynamically sized based on the current viewport. * @param {string} [options.localIdeographFontFamily='sans-serif'] Defines a CSS font-family for locally overriding generation of glyphs in the 'CJK Unified Ideographs', 'Hiragana', 'Katakana', 'Hangul Syllables' and 'CJK Symbols and Punctuation' ranges. - * In these ranges, font settings from the map's style will be ignored, except for font-weight keywords (light/regular/medium/bold). - * Set to `false`, to enable font settings from the map's style for these glyph ranges. Note that [Mapbox Studio](https://studio.mapbox.com/) sets this value to `false` by default. - * The purpose of this option is to avoid bandwidth-intensive glyph server requests. For an example of this option in use, see [Use locally generated ideographs](https://www.mapbox.com/mapbox-gl-js/example/local-ideographs). + * In these ranges, font settings from the map's style will be ignored, except for font-weight keywords (light/regular/medium/bold). + * Set to `false`, to enable font settings from the map's style for these glyph ranges. Note that [Mapbox Studio](https://studio.mapbox.com/) sets this value to `false` by default. + * The purpose of this option is to avoid bandwidth-intensive glyph server requests. For an example of this option in use, see [Use locally generated ideographs](https://www.mapbox.com/mapbox-gl-js/example/local-ideographs). * @param {string} [options.localFontFamily=null] Defines a CSS - * font-family for locally overriding generation of all glyphs. Font settings from the map's style will be ignored, except for font-weight keywords (light/regular/medium/bold). - * If set, this option overrides the setting in localIdeographFontFamily. + * font-family for locally overriding generation of all glyphs. Font settings from the map's style will be ignored, except for font-weight keywords (light/regular/medium/bold). + * If set, this option overrides the setting in localIdeographFontFamily. * @param {RequestTransformFunction} [options.transformRequest=null] A callback run before the Map makes a request for an external URL. The callback can be used to modify the url, set headers, or set the credentials property for cross-origin requests. - * Expected to return a {@link RequestParameters} object with a `url` property and optionally `headers` and `credentials` properties. + * Expected to return a {@link RequestParameters} object with a `url` property and optionally `headers` and `credentials` properties. * @param {boolean} [options.collectResourceTiming=false] If `true`, Resource Timing API information will be collected for requests made by GeoJSON and Vector Tile web workers (this information is normally inaccessible from the main Javascript thread). Information will be returned in a `resourceTiming` property of relevant `data` events. * @param {number} [options.fadeDuration=300] Controls the duration of the fade-in/fade-out animation for label collisions, in milliseconds. This setting affects all symbol layers. This setting does not affect the duration of runtime styling transitions or raster tile cross-fading. * @param {boolean} [options.respectPrefersReducedMotion=true] If set to `true`, the map will respect the user's `prefers-reduced-motion` browser setting and apply a reduced motion mode, minimizing animations and transitions. When set to `false`, the map will always ignore the `prefers-reduced-motion` settings, regardless of the user's preference, making all animations essential. * @param {boolean} [options.crossSourceCollisions=true] If `true`, symbols from multiple sources can collide with each other during collision detection. If `false`, collision detection is run separately for the symbols in each source. * @param {string} [options.accessToken=null] If specified, map will use this [token](https://docs.mapbox.com/help/glossary/access-token/) instead of the one defined in `mapboxgl.accessToken`. * @param {Object} [options.locale=null] A patch to apply to the default localization table for UI strings such as control tooltips. The `locale` object maps namespaced UI string IDs to translated strings in the target language; - * see [`src/ui/default_locale.js`](https://github.com/mapbox/mapbox-gl-js/blob/main/src/ui/default_locale.js) for an example with all supported string IDs. The object may specify all UI strings (thereby adding support for a new translation) or only a subset of strings (thereby patching the default translation table). + * see [`src/ui/default_locale.js`](https://github.com/mapbox/mapbox-gl-js/blob/main/src/ui/default_locale.js) for an example with all supported string IDs. The object may specify all UI strings (thereby adding support for a new translation) or only a subset of strings (thereby patching the default translation table). * @param {boolean} [options.testMode=false] Silences errors and warnings generated due to an invalid accessToken, useful when using the library to write unit tests. * @param {ProjectionSpecification} [options.projection='mercator'] The [projection](https://docs.mapbox.com/mapbox-gl-js/style-spec/projection/) the map should be rendered in. - * Supported projections are: - * * [Albers](https://en.wikipedia.org/wiki/Albers_projection) equal-area conic projection as `albers` - * * [Equal Earth](https://en.wikipedia.org/wiki/Equal_Earth_projection) equal-area pseudocylindrical projection as `equalEarth` - * * [Equirectangular](https://en.wikipedia.org/wiki/Equirectangular_projection) (Plate Carrée/WGS84) as `equirectangular` - * * 3d Globe as `globe` - * * [Lambert Conformal Conic](https://en.wikipedia.org/wiki/Lambert_conformal_conic_projection) as `lambertConformalConic` - * * [Mercator](https://en.wikipedia.org/wiki/Mercator_projection) cylindrical map projection as `mercator` - * * [Natural Earth](https://en.wikipedia.org/wiki/Natural_Earth_projection) pseudocylindrical map projection as `naturalEarth` - * * [Winkel Tripel](https://en.wikipedia.org/wiki/Winkel_tripel_projection) azimuthal map projection as `winkelTripel` - * Conic projections such as Albers and Lambert have configurable `center` and `parallels` properties that allow developers to define the region in which the projection has minimal distortion; see the example for how to configure these properties. + * Supported projections are: + * * [Albers](https://en.wikipedia.org/wiki/Albers_projection) equal-area conic projection as `albers` + * * [Equal Earth](https://en.wikipedia.org/wiki/Equal_Earth_projection) equal-area pseudocylindrical projection as `equalEarth` + * * [Equirectangular](https://en.wikipedia.org/wiki/Equirectangular_projection) (Plate Carrée/WGS84) as `equirectangular` + * * 3d Globe as `globe` + * * [Lambert Conformal Conic](https://en.wikipedia.org/wiki/Lambert_conformal_conic_projection) as `lambertConformalConic` + * * [Mercator](https://en.wikipedia.org/wiki/Mercator_projection) cylindrical map projection as `mercator` + * * [Natural Earth](https://en.wikipedia.org/wiki/Natural_Earth_projection) pseudocylindrical map projection as `naturalEarth` + * * [Winkel Tripel](https://en.wikipedia.org/wiki/Winkel_tripel_projection) azimuthal map projection as `winkelTripel` + * Conic projections such as Albers and Lambert have configurable `center` and `parallels` properties that allow developers to define the region in which the projection has minimal distortion; see the example for how to configure these properties. * @example * const map = new mapboxgl.Map({ * container: 'map', // container ID @@ -342,7 +360,7 @@ const defaultOptions = { * @see [Example: Display a map with a custom style](https://docs.mapbox.com/mapbox-gl-js/example/custom-style-id/) * @see [Example: Check if Mapbox GL JS is supported](https://docs.mapbox.com/mapbox-gl-js/example/check-for-support/) */ -class Map extends Camera { +export class Map extends Camera { style: Style; painter: Painter; handlers: ?HandlerManager; @@ -354,6 +372,7 @@ class Map extends Camera { _controlPositions: {[_: string]: HTMLElement}; _interactive: ?boolean; _showTileBoundaries: ?boolean; + _showParseStatus: ?boolean; _showTerrainWireframe: ?boolean; _showLayers2DWireframe: ?boolean; @@ -470,9 +489,14 @@ class Map extends Camera { */ touchPitch: TouchPitchHandler; + _contextCreateOptions: ContextOptions; + _tp: ITrackedParameters; + constructor(options: MapOptions) { LivePerformanceUtils.mark(PerformanceMarkers.create); + const initialOptions = options; + options = (extend({}, defaultOptions, options): typeof defaultOptions & MapOptions); if (options.minZoom != null && options.maxZoom != null && options.minZoom > options.maxZoom) { @@ -500,6 +524,7 @@ class Map extends Camera { const transform = new Transform(options.minZoom, options.maxZoom, options.minPitch, options.maxPitch, options.renderWorldCopies); super(transform, options); + this._repaint = !!options.repaint; this._interactive = options.interactive; this._minTileCacheSize = options.minTileCacheSize; this._maxTileCacheSize = options.maxTileCacheSize; @@ -527,6 +552,7 @@ class Map extends Camera { this._performanceMetricsCollection = options.performanceMetricsCollection; this._containerWidth = 0; this._containerHeight = 0; + this._showParseStatus = true; this._averageElevationLastSampledAt = -Infinity; this._averageElevationExaggeration = 0; @@ -539,14 +565,21 @@ class Map extends Camera { this._requestManager = new RequestManager(options.transformRequest, options.accessToken, options.testMode); this._silenceAuthErrors = !!options.testMode; + if (options.contextCreateOptions) { + this._contextCreateOptions = {...options.contextCreateOptions}; + } else { + this._contextCreateOptions = {}; + } if (typeof options.container === 'string') { - this._container = window.document.getElementById(options.container); - - if (!this._container) { + const container = document.getElementById(options.container); + if (container) { + this._container = container; + } else { throw new Error(`Container '${options.container.toString()}' not found.`); } - } else if (options.container instanceof window.HTMLElement) { + + } else if (options.container instanceof HTMLElement) { this._container = options.container; } else { throw new Error(`Invalid type: 'container' must be a String or HTMLElement.`); @@ -570,6 +603,25 @@ class Map extends Camera { ], this); this._setupContainer(); + if (options.devtools) { + this._tp = new TP.TrackedParameters(this); + } else { + this._tp = new TP.TrackedParametersMock(); + } + this._tp.registerParameter(this, ["Debug"], "showOverdrawInspector"); + this._tp.registerParameter(this, ["Debug"], "showTileBoundaries"); + this._tp.registerParameter(this, ["Debug"], "showParseStatus"); + this._tp.registerParameter(this, ["Debug"], "repaint"); + this._tp.registerParameter(this, ["Debug"], "showTileAABBs"); + this._tp.registerParameter(this, ["Debug"], "showPadding"); + this._tp.registerParameter(this, ["Debug"], "showCollisionBoxes", {noSave: true}); + this._tp.registerParameter(this.transform, ["Debug"], "freezeTileCoverage", {noSave: true}, () => { + this._update(); + }); + this._tp.registerParameter(this, ["Debug", "Wireframe"], "showTerrainWireframe"); + this._tp.registerParameter(this, ["Debug", "Wireframe"], "showLayers2DWireframe"); + this._tp.registerParameter(this, ["Debug", "Wireframe"], "showLayers3DWireframe"); + this._setupPainter(); if (this.painter === undefined) { throw new Error(`Failed to initialize WebGL.`); @@ -579,22 +631,20 @@ class Map extends Camera { this.on('moveend', () => this._update(false)); this.on('zoom', () => this._update(true)); - if (typeof window !== 'undefined') { - this._fullscreenchangeEvent = 'onfullscreenchange' in window.document ? - 'fullscreenchange' : - 'webkitfullscreenchange'; + this._fullscreenchangeEvent = 'onfullscreenchange' in document ? + 'fullscreenchange' : + 'webkitfullscreenchange'; - // $FlowFixMe[method-unbinding] - window.addEventListener('online', this._onWindowOnline, false); - // $FlowFixMe[method-unbinding] - window.addEventListener('resize', this._onWindowResize, false); - // $FlowFixMe[method-unbinding] - window.addEventListener('orientationchange', this._onWindowResize, false); - // $FlowFixMe[method-unbinding] - window.addEventListener(this._fullscreenchangeEvent, this._onWindowResize, false); - // $FlowFixMe[method-unbinding] - window.addEventListener('visibilitychange', this._onVisibilityChange, false); - } + // $FlowFixMe[method-unbinding] + window.addEventListener('online', this._onWindowOnline, false); + // $FlowFixMe[method-unbinding] + window.addEventListener('resize', this._onWindowResize, false); + // $FlowFixMe[method-unbinding] + window.addEventListener('orientationchange', this._onWindowResize, false); + // $FlowFixMe[method-unbinding] + window.addEventListener(this._fullscreenchangeEvent, this._onWindowResize, false); + // $FlowFixMe[method-unbinding] + window.addEventListener('visibilitychange', this._onVisibilityChange, false); this.handlers = new HandlerManager(this, options); @@ -614,6 +664,11 @@ class Map extends Camera { if (options.hash) this._hash = (new Hash(hashName)).addTo(this); // don't set position from options if set through hash if (!this._hash || !this._hash._onHashChange()) { + // if we set `center`/`zoom` explicitly, mark as modified even if the values match defaults + if (initialOptions.center != null || initialOptions.zoom != null) { + this.transform._unmodified = false; + } + this.jumpTo({ center: options.center, zoom: options.zoom, @@ -643,6 +698,7 @@ class Map extends Camera { if (this.transform.unmodified) { this.jumpTo((this.style.stylesheet: any)); } + this._postStyleLoadEvent(); }); this.on('data', (event: MapDataEvent) => { this._update(event.dataType === 'style'); @@ -670,7 +726,7 @@ class Map extends Camera { * * @param {IControl} control The {@link IControl} to add. * @param {string} [position] Position on the map to which the control will be added. - * Valid values are `'top-left'`, `'top-right'`, `'bottom-left'`, and `'bottom-right'`. Defaults to `'top-right'`. + * Valid values are `'top-left'`, `'top-right'`, `'bottom-left'`, and `'bottom-right'`. Defaults to `'top-right'`. * @returns {Map} Returns itself to allow for method chaining. * @example * // Add zoom and rotation controls to the map. @@ -798,8 +854,8 @@ class Map extends Camera { * or when the map is shown after being initially hidden with CSS. * * @param {Object | null} eventData Additional properties to be passed to `movestart`, `move`, `resize`, and `moveend` - * events that get triggered as a result of resize. This can be useful for differentiating the - * source of an event (for example, user-initiated or programmatically-triggered events). + * events that get triggered as a result of resize. This can be useful for differentiating the + * source of an event (for example, user-initiated or programmatically-triggered events). * @returns {Map} Returns itself to allow for method chaining. * @example * // Resize the map when the map container is shown @@ -898,7 +954,7 @@ class Map extends Camera { * no matter what the `minZoom` is set to. * * @param {number | null | undefined} minZoom The minimum zoom level to set (-2 - 24). - * If `null` or `undefined` is provided, the function removes the current minimum zoom and it will be reset to -2. + * If `null` or `undefined` is provided, the function removes the current minimum zoom and it will be reset to -2. * @returns {Map} Returns itself to allow for method chaining. * @example * map.setMinZoom(12.25); @@ -939,7 +995,7 @@ class Map extends Camera { * the map will zoom to the new maximum. * * @param {number | null | undefined} maxZoom The maximum zoom level to set. - * If `null` or `undefined` is provided, the function removes the current maximum zoom (sets it to 22). + * If `null` or `undefined` is provided, the function removes the current maximum zoom (sets it to 22). * @returns {Map} Returns itself to allow for method chaining. * @example * map.setMaxZoom(18.75); @@ -1024,7 +1080,7 @@ class Map extends Camera { * the map will pitch to the new maximum. * * @param {number | null | undefined} maxPitch The maximum pitch to set. - * If `null` or `undefined` is provided, the function removes the current maximum pitch (sets it to 85). + * If `null` or `undefined` is provided, the function removes the current maximum pitch (sets it to 85). * @returns {Map} Returns itself to allow for method chaining. * @example * map.setMaxPitch(70); @@ -1081,12 +1137,12 @@ class Map extends Camera { * Sets the state of `renderWorldCopies`. * * @param {boolean} renderWorldCopies If `true`, multiple copies of the world will be rendered side by side beyond -180 and 180 degrees longitude. If set to `false`: - * - When the map is zoomed out far enough that a single representation of the world does not fill the map's entire - * container, there will be blank space beyond 180 and -180 degrees longitude. - * - Features that cross 180 and -180 degrees longitude will be cut in two (with one portion on the right edge of the - * map and the other on the left edge of the map) at every zoom level. + * - When the map is zoomed out far enough that a single representation of the world does not fill the map's entire + * container, there will be blank space beyond 180 and -180 degrees longitude. + * - Features that cross 180 and -180 degrees longitude will be cut in two (with one portion on the right edge of the + * map and the other on the left edge of the map) at every zoom level. * - * `undefined` is treated as `true`, `null` is treated as `false`. + * `undefined` is treated as `true`, `null` is treated as `false`. * @returns {Map} Returns itself to allow for method chaining. * @example * map.setRenderWorldCopies(true); @@ -1113,10 +1169,10 @@ class Map extends Camera { } _parseLanguage(language?: 'auto' | ?string | ?string[]): ?string | ?string[] { - if (language === 'auto') return window.navigator.language; + if (language === 'auto') return navigator.language; if (Array.isArray(language)) return language.length === 0 ? undefined : - language.map(l => l === 'auto' ? window.navigator.language : l); + language.map(l => l === 'auto' ? navigator.language : l); return language; } @@ -1149,7 +1205,7 @@ class Map extends Camera { if (!this.style || newLanguage === this._language) return this; this._language = newLanguage; - this.style.clearSources(); + this.style.reloadSources(); for (const control of this._controls) { if (control._setLanguage) { @@ -1192,7 +1248,7 @@ class Map extends Camera { if (!this.style || worldview === this._worldview) return this; this._worldview = worldview; - this.style.clearSources(); + this.style.reloadSources(); return this; } @@ -1228,7 +1284,7 @@ class Map extends Camera { * Sets the map's projection. If called with `null` or `undefined`, the map will reset to Mercator. * * @param {ProjectionSpecification | string | null | undefined} projection The projection that the map should be rendered in. - * This can be a [projection](https://docs.mapbox.com/mapbox-gl-js/style-spec/projection/) object or a string of the projection's name. + * This can be a [projection](https://docs.mapbox.com/mapbox-gl-js/style-spec/projection/) object or a string of the projection's name. * @returns {Map} Returns itself to allow for method chaining. * @example * map.setProjection('albers'); @@ -1445,67 +1501,67 @@ class Map extends Camera { * optionally limited to features in a specified style layer. * * @param {string} type The event type to listen for. Events compatible with the optional `layerId` parameter are triggered - * when the cursor enters a visible portion of the specified layer from outside that layer or outside the map canvas. - * - * | Event | Compatible with `layerId` | - * |-----------------------------------------------------------|---------------------------| - * | [`mousedown`](#map.event:mousedown) | yes | - * | [`mouseup`](#map.event:mouseup) | yes | - * | [`mouseover`](#map.event:mouseover) | yes | - * | [`mouseout`](#map.event:mouseout) | yes | - * | [`mousemove`](#map.event:mousemove) | yes | - * | [`mouseenter`](#map.event:mouseenter) | yes (required) | - * | [`mouseleave`](#map.event:mouseleave) | yes (required) | - * | [`preclick`](#map.event:preclick) | | - * | [`click`](#map.event:click) | yes | - * | [`dblclick`](#map.event:dblclick) | yes | - * | [`contextmenu`](#map.event:contextmenu) | yes | - * | [`touchstart`](#map.event:touchstart) | yes | - * | [`touchend`](#map.event:touchend) | yes | - * | [`touchcancel`](#map.event:touchcancel) | yes | - * | [`wheel`](#map.event:wheel) | | - * | [`resize`](#map.event:resize) | | - * | [`remove`](#map.event:remove) | | - * | [`touchmove`](#map.event:touchmove) | | - * | [`movestart`](#map.event:movestart) | | - * | [`move`](#map.event:move) | | - * | [`moveend`](#map.event:moveend) | | - * | [`dragstart`](#map.event:dragstart) | | - * | [`drag`](#map.event:drag) | | - * | [`dragend`](#map.event:dragend) | | - * | [`zoomstart`](#map.event:zoomstart) | | - * | [`zoom`](#map.event:zoom) | | - * | [`zoomend`](#map.event:zoomend) | | - * | [`rotatestart`](#map.event:rotatestart) | | - * | [`rotate`](#map.event:rotate) | | - * | [`rotateend`](#map.event:rotateend) | | - * | [`pitchstart`](#map.event:pitchstart) | | - * | [`pitch`](#map.event:pitch) | | - * | [`pitchend`](#map.event:pitchend) | | - * | [`boxzoomstart`](#map.event:boxzoomstart) | | - * | [`boxzoomend`](#map.event:boxzoomend) | | - * | [`boxzoomcancel`](#map.event:boxzoomcancel) | | - * | [`webglcontextlost`](#map.event:webglcontextlost) | | - * | [`webglcontextrestored`](#map.event:webglcontextrestored) | | - * | [`load`](#map.event:load) | | - * | [`render`](#map.event:render) | | - * | [`idle`](#map.event:idle) | | - * | [`error`](#map.event:error) | | - * | [`data`](#map.event:data) | | - * | [`styledata`](#map.event:styledata) | | - * | [`sourcedata`](#map.event:sourcedata) | | - * | [`dataloading`](#map.event:dataloading) | | - * | [`styledataloading`](#map.event:styledataloading) | | - * | [`sourcedataloading`](#map.event:sourcedataloading) | | - * | [`styleimagemissing`](#map.event:styleimagemissing) | | - * | [`style.load`](#map.event:style.load) | | + * when the cursor enters a visible portion of the specified layer from outside that layer or outside the map canvas. + * + * | Event | Compatible with `layerId` | + * |-----------------------------------------------------------|---------------------------| + * | [`mousedown`](#map.event:mousedown) | yes | + * | [`mouseup`](#map.event:mouseup) | yes | + * | [`mouseover`](#map.event:mouseover) | yes | + * | [`mouseout`](#map.event:mouseout) | yes | + * | [`mousemove`](#map.event:mousemove) | yes | + * | [`mouseenter`](#map.event:mouseenter) | yes (required) | + * | [`mouseleave`](#map.event:mouseleave) | yes (required) | + * | [`preclick`](#map.event:preclick) | | + * | [`click`](#map.event:click) | yes | + * | [`dblclick`](#map.event:dblclick) | yes | + * | [`contextmenu`](#map.event:contextmenu) | yes | + * | [`touchstart`](#map.event:touchstart) | yes | + * | [`touchend`](#map.event:touchend) | yes | + * | [`touchcancel`](#map.event:touchcancel) | yes | + * | [`wheel`](#map.event:wheel) | | + * | [`resize`](#map.event:resize) | | + * | [`remove`](#map.event:remove) | | + * | [`touchmove`](#map.event:touchmove) | | + * | [`movestart`](#map.event:movestart) | | + * | [`move`](#map.event:move) | | + * | [`moveend`](#map.event:moveend) | | + * | [`dragstart`](#map.event:dragstart) | | + * | [`drag`](#map.event:drag) | | + * | [`dragend`](#map.event:dragend) | | + * | [`zoomstart`](#map.event:zoomstart) | | + * | [`zoom`](#map.event:zoom) | | + * | [`zoomend`](#map.event:zoomend) | | + * | [`rotatestart`](#map.event:rotatestart) | | + * | [`rotate`](#map.event:rotate) | | + * | [`rotateend`](#map.event:rotateend) | | + * | [`pitchstart`](#map.event:pitchstart) | | + * | [`pitch`](#map.event:pitch) | | + * | [`pitchend`](#map.event:pitchend) | | + * | [`boxzoomstart`](#map.event:boxzoomstart) | | + * | [`boxzoomend`](#map.event:boxzoomend) | | + * | [`boxzoomcancel`](#map.event:boxzoomcancel) | | + * | [`webglcontextlost`](#map.event:webglcontextlost) | | + * | [`webglcontextrestored`](#map.event:webglcontextrestored) | | + * | [`load`](#map.event:load) | | + * | [`render`](#map.event:render) | | + * | [`idle`](#map.event:idle) | | + * | [`error`](#map.event:error) | | + * | [`data`](#map.event:data) | | + * | [`styledata`](#map.event:styledata) | | + * | [`sourcedata`](#map.event:sourcedata) | | + * | [`dataloading`](#map.event:dataloading) | | + * | [`styledataloading`](#map.event:styledataloading) | | + * | [`sourcedataloading`](#map.event:sourcedataloading) | | + * | [`styleimagemissing`](#map.event:styleimagemissing) | | + * | [`style.load`](#map.event:style.load) | | * * @param {string | Array} layerIds (optional) The ID(s) of a style layer(s). If you provide a `layerId`, - * the listener will be triggered only if its location is within a visible feature in these layers, - * and the event will have a `features` property containing an array of the matching features. - * If you do not provide `layerIds`, the listener will be triggered by a corresponding event - * happening anywhere on the map, and the event will not have a `features` property. - * Note that many event types are not compatible with the optional `layerIds` parameter. + * the listener will be triggered only if its location is within a visible feature in these layers, + * and the event will have a `features` property containing an array of the matching features. + * If you do not provide `layerIds`, the listener will be triggered by a corresponding event + * happening anywhere on the map, and the event will not have a `features` property. + * Note that many event types are not compatible with the optional `layerIds` parameter. * @param {Function} listener The function to be called when the event is fired. * @returns {Map} Returns itself to allow for method chaining. * @example @@ -1553,7 +1609,7 @@ class Map extends Camera { * @see [Example: Create a hover effect](https://docs.mapbox.com/mapbox-gl-js/example/hover-styles/) * @see [Example: Display popup on click](https://docs.mapbox.com/mapbox-gl-js/example/popup-on-click/) */ - on(type: MapEvent, layerIds: any, listener: any): this { + on(type: MapEvent, layerIds: any, listener?: Listener): this { if (listener === undefined) { return super.on(type, layerIds); } @@ -1588,17 +1644,17 @@ class Map extends Camera { * optionally limited to events occurring on features in a specified style layer. * * @param {string} type The event type to listen for; one of `'mousedown'`, `'mouseup'`, `'preclick'`, `'click'`, `'dblclick'`, - * `'mousemove'`, `'mouseenter'`, `'mouseleave'`, `'mouseover'`, `'mouseout'`, `'contextmenu'`, `'touchstart'`, - * `'touchend'`, or `'touchcancel'`. `mouseenter` and `mouseover` events are triggered when the cursor enters - * a visible portion of the specified layer from outside that layer or outside the map canvas. `mouseleave` - * and `mouseout` events are triggered when the cursor leaves a visible portion of the specified layer, or leaves - * the map canvas. + * `'mousemove'`, `'mouseenter'`, `'mouseleave'`, `'mouseover'`, `'mouseout'`, `'contextmenu'`, `'touchstart'`, + * `'touchend'`, or `'touchcancel'`. `mouseenter` and `mouseover` events are triggered when the cursor enters + * a visible portion of the specified layer from outside that layer or outside the map canvas. `mouseleave` + * and `mouseout` events are triggered when the cursor leaves a visible portion of the specified layer, or leaves + * the map canvas. * @param {string | Array} layerIds (optional) The ID(s) of a style layer(s). If you provide `layerIds`, - * the listener will be triggered only if its location is within a visible feature in these layers, - * and the event will have a `features` property containing an array of the matching features. - * If you do not provide `layerIds`, the listener will be triggered by a corresponding event - * happening anywhere on the map, and the event will not have a `features` property. - * Note that many event types are not compatible with the optional `layerIds` parameter. + * the listener will be triggered only if its location is within a visible feature in these layers, + * and the event will have a `features` property containing an array of the matching features. + * If you do not provide `layerIds`, the listener will be triggered by a corresponding event + * happening anywhere on the map, and the event will not have a `features` property. + * Note that many event types are not compatible with the optional `layerIds` parameter. * @param {Function} listener The function to be called when the event is fired. * @returns {Map} Returns itself to allow for method chaining. * @example @@ -1622,7 +1678,7 @@ class Map extends Camera { * @see [Example: Animate the camera around a point with 3D terrain](https://docs.mapbox.com/mapbox-gl-js/example/free-camera-point/) * @see [Example: Play map locations as a slideshow](https://docs.mapbox.com/mapbox-gl-js/example/playback-locations/) */ - once(type: MapEvent, layerIds: any, listener: any): this | Promise { + once(type: MapEvent, layerIds: any, listener?: Listener): this | Promise { if (listener === undefined) { return super.once(type, layerIds); @@ -1674,7 +1730,7 @@ class Map extends Camera { * }); * @see [Example: Create a draggable point](https://docs.mapbox.com/mapbox-gl-js/example/drag-a-point/) */ - off(type: MapEvent, layerIds: any, listener: any): this { + off(type: MapEvent, layerIds: any, listener?: Listener): this { if (listener === undefined) { return super.off(type, layerIds); } @@ -1727,48 +1783,48 @@ class Map extends Camera { * representing visible features that satisfy the query parameters. * * @param {PointLike|Array} [geometry] - The geometry of the query region in pixels: - * either a single point or bottom left and top right points describing a bounding box, where the origin is at the top left. - * Omitting this parameter (by calling {@link Map#queryRenderedFeatures} with zero arguments, - * or with only an `options` argument) is equivalent to passing a bounding box encompassing the entire - * map viewport. - * Only values within the existing viewport are supported. + * either a single point or bottom left and top right points describing a bounding box, where the origin is at the top left. + * Omitting this parameter (by calling {@link Map#queryRenderedFeatures} with zero arguments, + * or with only an `options` argument) is equivalent to passing a bounding box encompassing the entire + * map viewport. + * Only values within the existing viewport are supported. * @param {Object} [options] Options object. * @param {Array} [options.layers] An array of [style layer IDs](https://docs.mapbox.com/mapbox-gl-js/style-spec/#layer-id) for the query to inspect. - * Only features within these layers will be returned. If this parameter is undefined, all layers will be checked. + * Only features within these layers will be returned. If this parameter is undefined, all layers will be checked. * @param {Array} [options.filter] A [filter](https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/#filter) - * to limit query results. + * to limit query results. * @param {boolean} [options.validate=true] Whether to check if the [options.filter] conforms to the Mapbox GL Style Specification. Disabling validation is a performance optimization that should only be used if you have previously validated the values you will be passing to this function. * * @returns {Array} An array of [GeoJSON](http://geojson.org/) - * [feature objects](https://tools.ietf.org/html/rfc7946#section-3.2). + * [feature objects](https://tools.ietf.org/html/rfc7946#section-3.2). * - * The `properties` value of each returned feature object contains the properties of its source feature. For GeoJSON sources, only - * string and numeric property values are supported. `null`, `Array`, and `Object` values are not supported. + * The `properties` value of each returned feature object contains the properties of its source feature. For GeoJSON sources, only + * string and numeric property values are supported. `null`, `Array`, and `Object` values are not supported. * - * Each feature includes top-level `layer`, `source`, and `sourceLayer` properties. The `layer` property is an object - * representing the style layer to which the feature belongs. Layout and paint properties in this object contain values - * which are fully evaluated for the given zoom level and feature. + * Each feature includes top-level `layer`, `source`, and `sourceLayer` properties. The `layer` property is an object + * representing the style layer to which the feature belongs. Layout and paint properties in this object contain values + * which are fully evaluated for the given zoom level and feature. * - * Only features that are currently rendered are included. Some features will **not** be included, like: + * Only features that are currently rendered are included. Some features will **not** be included, like: * - * - Features from layers whose `visibility` property is `"none"`. - * - Features from layers whose zoom range excludes the current zoom level. - * - Symbol features that have been hidden due to text or icon collision. + * - Features from layers whose `visibility` property is `"none"`. + * - Features from layers whose zoom range excludes the current zoom level. + * - Symbol features that have been hidden due to text or icon collision. * - * Features from all other layers are included, including features that may have no visible - * contribution to the rendered result; for example, because the layer's opacity or color alpha component is set to 0. + * Features from all other layers are included, including features that may have no visible + * contribution to the rendered result; for example, because the layer's opacity or color alpha component is set to 0. * - * The topmost rendered feature appears first in the returned array, and subsequent features are sorted by - * descending z-order. Features that are rendered multiple times (due to wrapping across the antimeridian at low - * zoom levels) are returned only once (though subject to the following caveat). + * The topmost rendered feature appears first in the returned array, and subsequent features are sorted by + * descending z-order. Features that are rendered multiple times (due to wrapping across the antimeridian at low + * zoom levels) are returned only once (though subject to the following caveat). * - * Because features come from tiled vector data or GeoJSON data that is converted to tiles internally, feature - * geometries may be split or duplicated across tile boundaries and, as a result, features may appear multiple - * times in query results. For example, suppose there is a highway running through the bounding rectangle of a query. - * The results of the query will be those parts of the highway that lie within the map tiles covering the bounding - * rectangle, even if the highway extends into other tiles, and the portion of the highway within each map tile - * will be returned as a separate feature. Similarly, a point feature near a tile boundary may appear in multiple - * tiles due to tile buffering. + * Because features come from tiled vector data or GeoJSON data that is converted to tiles internally, feature + * geometries may be split or duplicated across tile boundaries and, as a result, features may appear multiple + * times in query results. For example, suppose there is a highway running through the bounding rectangle of a query. + * The results of the query will be those parts of the highway that lie within the map tiles covering the bounding + * rectangle, even if the highway extends into other tiles, and the portion of the highway within each map tile + * will be returned as a separate feature. Similarly, a point feature near a tile boundary may appear in multiple + * tiles due to tile buffering. * * @example * // Find all features at a point @@ -1841,26 +1897,26 @@ class Map extends Camera { * @param {string} sourceId The ID of the vector tile or GeoJSON source to query. * @param {Object} [parameters] Options object. * @param {string} [parameters.sourceLayer] The name of the [source layer](https://docs.mapbox.com/help/glossary/source-layer/) - * to query. *For vector tile sources, this parameter is required.* For GeoJSON sources, it is ignored. + * to query. *For vector tile sources, this parameter is required.* For GeoJSON sources, it is ignored. * @param {Array} [parameters.filter] A [filter](https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/#filter) - * to limit query results. + * to limit query results. * @param {boolean} [parameters.validate=true] Whether to check if the [parameters.filter] conforms to the Mapbox GL Style Specification. Disabling validation is a performance optimization that should only be used if you have previously validated the values you will be passing to this function. * * @returns {Array} An array of [GeoJSON](http://geojson.org/) - * [Feature objects](https://tools.ietf.org/html/rfc7946#section-3.2). + * [Feature objects](https://tools.ietf.org/html/rfc7946#section-3.2). * - * In contrast to {@link Map#queryRenderedFeatures}, this function returns all features matching the query parameters, - * whether or not they are rendered by the current style (in other words, are visible). The domain of the query includes all currently-loaded - * vector tiles and GeoJSON source tiles: this function does not check tiles outside the currently - * visible viewport. + * In contrast to {@link Map#queryRenderedFeatures}, this function returns all features matching the query parameters, + * whether or not they are rendered by the current style (in other words, are visible). The domain of the query includes all currently-loaded + * vector tiles and GeoJSON source tiles: this function does not check tiles outside the currently + * visible viewport. * - * Because features come from tiled vector data or GeoJSON data that is converted to tiles internally, feature - * geometries may be split or duplicated across tile boundaries and, as a result, features may appear multiple - * times in query results. For example, suppose there is a highway running through the bounding rectangle of a query. - * The results of the query will be those parts of the highway that lie within the map tiles covering the bounding - * rectangle, even if the highway extends into other tiles, and the portion of the highway within each map tile - * will be returned as a separate feature. Similarly, a point feature near a tile boundary may appear in multiple - * tiles due to tile buffering. + * Because features come from tiled vector data or GeoJSON data that is converted to tiles internally, feature + * geometries may be split or duplicated across tile boundaries and, as a result, features may appear multiple + * times in query results. For example, suppose there is a highway running through the bounding rectangle of a query. + * The results of the query will be those parts of the highway that lie within the map tiles covering the bounding + * rectangle, even if the highway extends into other tiles, and the portion of the highway within each map tile + * will be returned as a separate feature. Similarly, a point feature near a tile boundary may appear in multiple + * tiles due to tile buffering. * * @example * // Find all features in one source layer in a vector source @@ -1907,15 +1963,15 @@ class Map extends Camera { * the given one from scratch. * * @param {Object | string| null} style A JSON object conforming to the schema described in the - * [Mapbox Style Specification](https://mapbox.com/mapbox-gl-style-spec/), or a URL to such JSON. + * [Mapbox Style Specification](https://mapbox.com/mapbox-gl-style-spec/), or a URL to such JSON. * @param {Object} [options] Options object. * @param {boolean} [options.diff=true] If false, force a 'full' update, removing the current style - * and building the given one instead of attempting a diff-based update. + * and building the given one instead of attempting a diff-based update. * @param {string} [options.localIdeographFontFamily='sans-serif'] Defines a CSS - * font-family for locally overriding generation of glyphs in the 'CJK Unified Ideographs', 'Hiragana', 'Katakana' and 'Hangul Syllables' ranges. - * In these ranges, font settings from the map's style will be ignored, except for font-weight keywords (light/regular/medium/bold). - * Set to `false`, to enable font settings from the map's style for these glyph ranges. - * Forces a full update. + * font-family for locally overriding generation of glyphs in the 'CJK Unified Ideographs', 'Hiragana', 'Katakana' and 'Hangul Syllables' ranges. + * In these ranges, font settings from the map's style will be ignored, except for font-weight keywords (light/regular/medium/bold). + * Set to `false`, to enable font settings from the map's style for these glyph ranges. + * Forces a full update. * @returns {Map} Returns itself to allow for method chaining. * * @example @@ -1929,7 +1985,21 @@ class Map extends Camera { if ((options.diff !== false && options.localIdeographFontFamily === this._localIdeographFontFamily && options.localFontFamily === this._localFontFamily) && this.style && style) { - this._diffStyle(style, options); + this.style._diffStyle( + style, + (e: any, isUpdateNeeded) => { + if (e) { + warnOnce( + `Unable to perform style diff: ${e.message || e.error || e}. Rebuilding the style from scratch.` + ); + this._updateStyle(style, options); + } else if (isUpdateNeeded) { + this._update(true); + } + }, + () => { + this._postStyleLoadEvent(); + }); return this; } else { this._localIdeographFontFamily = options.localIdeographFontFamily; @@ -1955,15 +2025,11 @@ class Map extends Camera { } if (style) { - this.style = new Style(this, options || {}); - this.style.setEventedParent(this, {style: this.style}); - - if (typeof style === 'string') { - this.style.loadURL(style); - } else { - this.style.loadJSON(style); - } + this.style = new Style(this, options) + .setEventedParent(this, {style: this.style}) + .load(style); } + this._updateTerrain(); return this; } @@ -1976,35 +2042,6 @@ class Map extends Camera { } } - _diffStyle(style: StyleSpecification | string, options?: {diff?: boolean} & StyleOptions) { - if (typeof style === 'string') { - const url = this._requestManager.normalizeStyleURL(style); - const request = this._requestManager.transformRequest(url, ResourceType.Style); - getJSON(request, (error: ?Error, json: ?Object) => { - if (error) { - this.fire(new ErrorEvent(error)); - } else if (json) { - this._updateDiff(json, options); - } - }); - } else if (typeof style === 'object') { - this._updateDiff(style, options); - } - } - - _updateDiff(style: StyleSpecification, options?: {diff?: boolean} & StyleOptions) { - try { - if (this.style.setState(style)) { - this._update(true); - } - } catch (e) { - warnOnce( - `Unable to perform style diff: ${e.message || e.error || e}. Rebuilding the style from scratch.` - ); - this._updateStyle(style, options); - } - } - /** * Returns the map's Mapbox [style](https://docs.mapbox.com/help/glossary/style/) object, a JSON object which can be used to recreate the map's style. * @@ -2037,7 +2074,12 @@ class Map extends Camera { return this.style.loaded(); } - _isValidId(id: string): boolean { + _isValidId(id: ?string): boolean { + if (id == null) { + this.fire(new ErrorEvent(new Error(`IDs can't be empty.`))); + return false; + } + // Disallow using fully qualified IDs in the public APIs if (isFQID(id)) { this.fire(new ErrorEvent(new Error(`IDs can't contain special symbols: "${id}".`))); @@ -2054,8 +2096,8 @@ class Map extends Camera { * * @param {string} id The ID of the source to add. Must not conflict with existing sources. * @param {Object} source The source object, conforming to the - * Mapbox Style Specification's [source definition](https://www.mapbox.com/mapbox-gl-style-spec/#sources) or - * {@link CanvasSourceOptions}. + * Mapbox Style Specification's [source definition](https://www.mapbox.com/mapbox-gl-style-spec/#sources) or + * {@link CanvasSourceOptions}. * @returns {Map} Returns itself to allow for method chaining. * @example * map.addSource('my-data', { @@ -2116,18 +2158,8 @@ class Map extends Camera { * @example * const tilesLoaded = map.areTilesLoaded(); */ - areTilesLoaded(): boolean { - const sources = this.style && this.style._sourceCaches; - for (const id in sources) { - const source = sources[id]; - const tiles = source._tiles; - for (const t in tiles) { - const tile = tiles[t]; - if (!(tile.state === 'loaded' || tile.state === 'errored')) return false; - } - } - return true; + return this.style.areTilesLoaded(); } /** @@ -2171,10 +2203,10 @@ class Map extends Camera { * * @param {string} id The ID of the source to get. * @returns {?Object} The style source with the specified ID or `undefined` if the ID - * corresponds to no existing sources. - * The shape of the object varies by source type. - * A list of options for each source type is available on the Mapbox Style Specification's - * [Sources](https://docs.mapbox.com/mapbox-gl-js/style-spec/sources/) page. + * corresponds to no existing sources. + * The shape of the object varies by source type. + * A list of options for each source type is available on the Mapbox Style Specification's + * [Sources](https://docs.mapbox.com/mapbox-gl-js/style-spec/sources/) page. * @example * const sourceObject = map.getSource('points'); * @see [Example: Create a draggable point](https://docs.mapbox.com/mapbox-gl-js/example/drag-a-point/) @@ -2203,7 +2235,7 @@ class Map extends Camera { * * @param {string} id The ID of the image. * @param {HTMLImageElement | ImageBitmap | ImageData | {width: number, height: number, data: (Uint8Array | Uint8ClampedArray)} | StyleImageInterface} image The image as an `HTMLImageElement`, `ImageData`, `ImageBitmap` or object with `width`, `height`, and `data` - * properties with the same format as `ImageData`. + * properties with the same format as `ImageData`. * @param {Object | null} options Options object. * @param {number} options.pixelRatio The ratio of pixels in the image to physical pixels on the screen. * @param {boolean} options.sdf Whether the image should be interpreted as an SDF image. @@ -2242,7 +2274,7 @@ class Map extends Camera { this._lazyInitEmptyStyle(); const version = 0; - if (image instanceof window.HTMLImageElement || (window.ImageBitmap && image instanceof window.ImageBitmap)) { + if (image instanceof HTMLImageElement || (ImageBitmap && image instanceof ImageBitmap)) { const {width, height, data} = browser.getImageData(image); this.style.addImage(id, {data: new RGBAImage({width, height}, data), pixelRatio, stretchX, stretchY, content, sdf, version}); } else if (image.width === undefined || image.height === undefined) { @@ -2282,7 +2314,7 @@ class Map extends Camera { * * @param {string} id The ID of the image. * @param {HTMLImageElement | ImageBitmap | ImageData | StyleImageInterface} image The image as an `HTMLImageElement`, [`ImageData`](https://developer.mozilla.org/en-US/docs/Web/API/ImageData), [`ImageBitmap`](https://developer.mozilla.org/en-US/docs/Web/API/ImageBitmap) or object with `width`, `height`, and `data` - * properties with the same format as `ImageData`. + * properties with the same format as `ImageData`. * * @example * // Load an image from an external URL. @@ -2303,7 +2335,7 @@ class Map extends Camera { 'The map has no image with that id. If you are adding a new image use `map.addImage(...)` instead.'))); return; } - const imageData = (image instanceof window.HTMLImageElement || (window.ImageBitmap && image instanceof window.ImageBitmap)) ? browser.getImageData(image) : image; + const imageData = (image instanceof HTMLImageElement || (ImageBitmap && image instanceof ImageBitmap)) ? browser.getImageData(image) : image; const {width, height} = imageData; // Flow can't refine the type enough to exclude ImageBitmap const data = ((imageData: any).data: Uint8Array | Uint8ClampedArray); @@ -2323,7 +2355,7 @@ class Map extends Camera { return; } - const copy = !(image instanceof window.HTMLImageElement || (window.ImageBitmap && image instanceof window.ImageBitmap)); + const copy = !(image instanceof HTMLImageElement || (ImageBitmap && image instanceof ImageBitmap)); existingImage.data.replace(data, copy); this.style.updateImage(id, existingImage); @@ -2388,7 +2420,7 @@ class Map extends Camera { */ loadImage(url: string, callback: Function) { getImage(this._requestManager.transformRequest(url, ResourceType.Image), (err, img) => { - callback(err, img instanceof window.HTMLImageElement ? browser.getImageData(img) : img); + callback(err, img instanceof HTMLImageElement ? browser.getImageData(img) : img); }); } @@ -2505,47 +2537,63 @@ class Map extends Camera { * and available paint and layout properties in the [Mapbox Style Specification](https://docs.mapbox.com/mapbox-gl-js/style-spec/#layers). * * @param {Object | CustomLayerInterface} layer The layer to add, conforming to either the Mapbox Style Specification's [layer definition](https://docs.mapbox.com/mapbox-gl-js/style-spec/#layers) or, less commonly, the {@link CustomLayerInterface} specification. - * The Mapbox Style Specification's layer definition is appropriate for most layers. + * The Mapbox Style Specification's layer definition is appropriate for most layers. * * @param {string} layer.id A unique identifier that you define. * @param {string} layer.type The type of layer (for example `fill` or `symbol`). - * A list of layer types is available in the [Mapbox Style Specification](https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/#type). + * A list of layer types is available in the [Mapbox Style Specification](https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/#type). * - * This can also be `custom`. For more information, see {@link CustomLayerInterface}. + * This can also be `custom`. For more information, see {@link CustomLayerInterface}. * @param {string | Object} [layer.source] The data source for the layer. - * Reference a source that has _already been defined_ using the source's unique id. - * Reference a _new source_ using a source object (as defined in the [Mapbox Style Specification](https://docs.mapbox.com/mapbox-gl-js/style-spec/sources/)) directly. - * This is **required** for all `layer.type` options _except_ for `custom` and `background`. + * Reference a source that has _already been defined_ using the source's unique id. + * Reference a _new source_ using a source object (as defined in the [Mapbox Style Specification](https://docs.mapbox.com/mapbox-gl-js/style-spec/sources/)) directly. + * This is **required** for all `layer.type` options _except_ for `custom` and `background`. * @param {string} [layer.sourceLayer] (optional) The name of the [source layer](https://docs.mapbox.com/help/glossary/source-layer/) within the specified `layer.source` to use for this style layer. - * This is only applicable for vector tile sources and is **required** when `layer.source` is of the type `vector`. + * This is only applicable for vector tile sources and is **required** when `layer.source` is of the type `vector`. + * @param {string} [layer.slot] (optional) The identifier of a [`slot`](https://docs.mapbox.com/style-spec/reference/slots/) layer that will be used to position this style layer. + * A `slot` layer serves as a predefined position in the layer order for inserting associated layers. + * *Note*: During 3D globe and terrain rendering, GL JS aims to batch multiple layers together for optimal performance. + * This process might lead to a rearrangement of layers. Layers draped over globe and terrain, + * such as `fill`, `line`, `background`, `hillshade`, and `raster`, are rendered first. + * These layers are rendered underneath symbols, regardless of whether they are placed + * in the middle or top slots or without a designated slot. * @param {Array} [layer.filter] (optional) An expression specifying conditions on source features. - * Only features that match the filter are displayed. - * The Mapbox Style Specification includes more information on the limitations of the [`filter`](https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/#filter) parameter - * and a complete list of available [expressions](https://docs.mapbox.com/mapbox-gl-js/style-spec/expressions/). - * If no filter is provided, all features in the source (or source layer for vector tilesets) will be displayed. + * Only features that match the filter are displayed. + * The Mapbox Style Specification includes more information on the limitations of the [`filter`](https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/#filter) parameter + * and a complete list of available [expressions](https://docs.mapbox.com/mapbox-gl-js/style-spec/expressions/). + * If no filter is provided, all features in the source (or source layer for vector tilesets) will be displayed. * @param {Object} [layer.paint] (optional) Paint properties for the layer. - * Available paint properties vary by `layer.type`. - * A full list of paint properties for each layer type is available in the [Mapbox Style Specification](https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/). - * If no paint properties are specified, default values will be used. + * Available paint properties vary by `layer.type`. + * A full list of paint properties for each layer type is available in the [Mapbox Style Specification](https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/). + * If no paint properties are specified, default values will be used. * @param {Object} [layer.layout] (optional) Layout properties for the layer. - * Available layout properties vary by `layer.type`. - * A full list of layout properties for each layer type is available in the [Mapbox Style Specification](https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/). - * If no layout properties are specified, default values will be used. + * Available layout properties vary by `layer.type`. + * A full list of layout properties for each layer type is available in the [Mapbox Style Specification](https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/). + * If no layout properties are specified, default values will be used. * @param {number} [layer.maxzoom] (optional) The maximum zoom level for the layer. - * At zoom levels equal to or greater than the maxzoom, the layer will be hidden. - * The value can be any number between `0` and `24` (inclusive). - * If no maxzoom is provided, the layer will be visible at all zoom levels for which there are tiles available. + * At zoom levels equal to or greater than the maxzoom, the layer will be hidden. + * The value can be any number between `0` and `24` (inclusive). + * If no maxzoom is provided, the layer will be visible at all zoom levels for which there are tiles available. * @param {number} [layer.minzoom] (optional) The minimum zoom level for the layer. - * At zoom levels less than the minzoom, the layer will be hidden. - * The value can be any number between `0` and `24` (inclusive). - * If no minzoom is provided, the layer will be visible at all zoom levels for which there are tiles available. + * At zoom levels less than the minzoom, the layer will be hidden. + * The value can be any number between `0` and `24` (inclusive). + * If no minzoom is provided, the layer will be visible at all zoom levels for which there are tiles available. * @param {Object} [layer.metadata] (optional) Arbitrary properties useful to track with the layer, but do not influence rendering. * @param {string} [layer.renderingMode] This is only applicable for layers with the type `custom`. - * See {@link CustomLayerInterface} for more information. + * See {@link CustomLayerInterface} for more information. * @param {string} [beforeId] The ID of an existing layer to insert the new layer before, - * resulting in the new layer appearing visually beneath the existing layer. - * If this argument is not specified, the layer will be appended to the end of the layers array - * and appear visually above all other layers. + * resulting in the new layer appearing visually beneath the existing layer. + * If this argument is not specified, the layer will be appended to the end of the layers array + * and appear visually above all other layers. + * *Note*: Layers can only be rearranged within the same `slot`. The new layer must share the + * same `slot` as the existing layer to be positioned underneath it. If the + * layers are in different slots, the `beforeId` parameter will be ignored and + * the new layer will be appended to the end of the layers array. + * During 3D globe and terrain rendering, GL JS aims to batch multiple layers together for optimal performance. + * This process might lead to a rearrangement of layers. Layers draped over globe and terrain, + * such as `fill`, `line`, `background`, `hillshade`, and `raster`, are rendered first. + * These layers are rendered underneath symbols, regardless of whether they are placed + * in the middle or top slots or without a designated slot. * * @returns {Map} Returns itself to allow for method chaining. * @@ -2588,6 +2636,22 @@ class Map extends Camera { * }); * * @example + * // Add a new symbol layer to a slot + * map.addLayer({ + * id: 'states', + * // References a source that's already been defined + * source: 'state-data', + * type: 'symbol', + * // Add the layer to the existing `top` slot + * slot: 'top', + * layout: { + * // Set the label content to the + * // feature's `name` property + * 'text-field': ['get', 'name'] + * } + * }); + * + * @example * // Add a new symbol layer before an existing layer * map.addLayer({ * id: 'states', @@ -2618,11 +2682,181 @@ class Map extends Camera { return this._update(true); } + /** + * Returns current slot of the layer. + * + * @param {string} layerId Identifier of the layer to retrieve its current slot. + * @returns {string | null} The slot identifier or `null` if layer doesn't have it. + * + * @example + * map.getSlot('roads'); + */ + getSlot(layerId: string): ?string { + const layer = this.getLayer(layerId); + + if (!layer) { + return null; + } + + return layer.slot || null; + } + + /** + * Sets or removes [a slot](https://docs.mapbox.com/style-spec/reference/slots/) of style layer. + * + * @param {string} layerId Identifier of style layer. + * @param {string} slot Identifier of slot. If `null` or `undefined` is provided, the method removes slot. + * @returns {Map} Returns itself to allow for method chaining. + * + * @example + * // Sets new slot for style layer + * map.setSlot("heatmap", "top"); + */ + setSlot(layerId: string, slot: ?string): this { + this.style.setSlot(layerId, slot); + this.style.mergeLayers(); + return this._update(true); + } + + /** + * Adds new [import](https://docs.mapbox.com/style-spec/reference/imports/) to current style. + * + * @param {ImportSpecification} importSpecification Specification of import. + * @param {string} beforeId (optional) Identifier of an existing import to insert the new import before. + * @returns {Map} Returns itself to allow for method chaining. + * + * @example + * // Add streets style to empty map + * new Map({style: {version: 8, sources: {}, layers: []}}) + * .addImport({id: 'basemap', url: 'mapbox://styles/mapbox/streets-v12'}); + * + * @example + * // Add new style before already added + * const map = new Map({ + * imports: [ + * { + * id: 'basemap', + * url: 'mapbox://styles/mapbox/standard' + * } + * ], + * style: { + * version: 8, + * sources: {}, + * layers: [] + * } + * }); + * + * map.addImport({ + * id: 'lakes', + * url: 'https://styles/mapbox/streets-v12' + * }, 'basemap'); + */ + addImport(importSpecification: ImportSpecification, beforeId: ?string): this { + this.style.addImport(importSpecification, beforeId); + return this; + } + + /** + * Updates already added to style import. + * + * @param {string} importId Identifier of import to update. + * @param {ImportSpecification | string} importSpecification Import specification or URL of style. + * @returns {Map} Returns itself to allow for method chaining. + * + * @example + * // Update import with new data + * map.updateImport('basemap', { + * data: { + * version: 8, + * sources: {}, + * layers: [ + * { + * id: 'background', + * type: 'background', + * paint: { + * 'background-color': '#eee' + * } + * } + * ] + * } + * }); + * + * @example + * // Change URL of imported style + * map.updateImport('basemap', 'mapbox://styles/mapbox/other-standard'); + */ + updateImport(importId: string, importSpecification: ImportSpecification | string): this { + if (typeof importSpecification !== 'string' && importSpecification.id !== importId) { + this.removeImport(importId); + return this.addImport(importSpecification); + } + + this.style.updateImport(importId, importSpecification); + return this._update(true); + } + + /** + * Removes added to style import. + * + * @param {string} importId Identifier of import to remove. + * @returns {Map} Returns itself to allow for method chaining. + * + * @example + * // Removes imported style + * map.removeImport('basemap'); + */ + removeImport(importId: string): this { + this.style.removeImport(importId); + return this; + } + + /** + * Moves import to position before another import, specified with `beforeId`. Order of imported styles corresponds to order of their layers. + * + * @param {string} importId Identifier of import to move. + * @param {string} beforeId The identifier of an existing import to move the new import before. + * @returns {Map} Returns itself to allow for method chaining. + * + * @example + * const map = new Map({ + * style: { + * imports: [ + * { + * id: 'basemap', + * url: 'mapbox://styles/mapbox/standard' + * }, + * { + * id: 'streets-v12', + * url: 'mapbox://styles/mapbox/streets-v12' + * } + * ] + * } + * }); + * // Place `streets-v12` import before `basemap` + * map.moveImport('streets-v12', 'basemap'); + */ + moveImport(importId: string, beforeId: string): this { + this.style.moveImport(importId, beforeId); + return this._update(true); + } + /** * Moves a layer to a different z-position. * * @param {string} id The ID of the layer to move. - * @param {string} [beforeId] The ID of an existing layer to insert the new layer before. When viewing the map, the `id` layer will appear beneath the `beforeId` layer. If `beforeId` is omitted, the layer will be appended to the end of the layers array and appear above all other layers on the map. + * @param {string} [beforeId] The ID of an existing layer to insert the new layer before. + * When viewing the map, the `id` layer will appear beneath the `beforeId` layer. + * If `beforeId` is omitted, the layer will be appended to the end of the layers array + * and appear above all other layers on the map. + * *Note*: Layers can only be rearranged within the same `slot`. The new layer must share the + * same `slot` as the existing layer to be positioned underneath it. If the + * layers are in different slots, the `beforeId` parameter will be ignored and + * the new layer will be appended to the end of the layers array. + * During 3D globe and terrain rendering, GL JS aims to batch multiple layers together for optimal performance. + * This process might lead to a rearrangement of layers. Layers draped over globe and terrain, + * such as `fill`, `line`, `background`, `hillshade`, and `raster`, are rendered first. + * These layers are rendered underneath symbols, regardless of whether they are placed + * in the middle or top slots or without a designated slot. * @returns {Map} Returns itself to allow for method chaining. * * @example @@ -2665,7 +2899,7 @@ class Map extends Camera { * * @param {string} id The ID of the layer to get. * @returns {?Object} The layer with the specified ID, or `undefined` - * if the ID corresponds to no existing layers. + * if the ID corresponds to no existing layers. * * @example * const stateDataLayer = map.getLayer('state-data'); @@ -2722,7 +2956,7 @@ class Map extends Camera { * * @param {string} layerId The ID of the layer to which the filter will be applied. * @param {Array | null | undefined} filter The filter, conforming to the Mapbox Style Specification's - * [filter definition](https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/#filter). If `null` or `undefined` is provided, the function removes any existing filter from the layer. + * [filter definition](https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/#filter). If `null` or `undefined` is provided, the function removes any existing filter from the layer. * @param {Object} [options] Options object. * @param {boolean} [options.validate=true] Whether to check if the filter conforms to the Mapbox GL Style Specification. Disabling validation is a performance optimization that should only be used if you have previously validated the values you will be passing to this function. * @returns {Map} Returns itself to allow for method chaining. @@ -2773,7 +3007,7 @@ class Map extends Camera { * @param {string} layerId The ID of the layer to set the paint property in. * @param {string} name The name of the paint property to set. * @param {*} value The value of the paint property to set. - * Must be of a type appropriate for the property, as defined in the [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/). + * Must be of a type appropriate for the property, as defined in the [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/). * @param {Object} [options] Options object. * @param {boolean} [options.validate=true] Whether to check if `value` conforms to the Mapbox GL Style Specification. Disabling validation is a performance optimization that should only be used if you have previously validated the values you will be passing to this function. * @returns {Map} Returns itself to allow for method chaining. @@ -2850,6 +3084,19 @@ class Map extends Camera { /** @section {Style properties} */ + /** + * Returns the value of a configuration property in the imported style. + * + * @param {string} importId The name of the imported style to set the config for (e.g. `basemap`). + * @param {string} configName The name of the configuration property from the style. + * @returns {*} Returns the value of the configuration property. + * @example + * map.getConfigProperty('basemap', 'showLabels'); + */ + getConfigProperty(importId: string, configName: string): ?any { + return this.style.getConfigProperty(importId, configName); + } + /** * Sets the value of a configuration property in the currently set style. * @@ -2967,7 +3214,8 @@ class Map extends Camera { * Sets the terrain property of the style. * * @param {TerrainSpecification} terrain Terrain properties to set. Must conform to the [Terrain Style Specification](https://docs.mapbox.com/mapbox-gl-js/style-spec/terrain/). - * If `null` or `undefined` is provided, function removes terrain. + * If `null` or `undefined` is provided, function removes terrain. + * Exaggeration could be updated for the existing terrain without explicitly specifying the `source`. * @returns {Map} Returns itself to allow for method chaining. * @example * map.addSource('mapbox-dem', { @@ -2978,6 +3226,8 @@ class Map extends Camera { * }); * // add the DEM source as a terrain layer with exaggerated height * map.setTerrain({'source': 'mapbox-dem', 'exaggeration': 1.5}); + * // update the exaggeration for the existing terrain + * map.setTerrain({'exaggeration': 2}); */ setTerrain(terrain: TerrainSpecification): this { this._lazyInitEmptyStyle(); @@ -3005,7 +3255,7 @@ class Map extends Camera { * Sets the fog property of the style. * * @param {FogSpecification} fog The fog properties to set. Must conform to the [Fog Style Specification](https://docs.mapbox.com/mapbox-gl-js/style-spec/fog/). - * If `null` or `undefined` is provided, this function call removes the fog from the map. + * If `null` or `undefined` is provided, this function call removes the fog from the map. * @returns {Map} Returns itself to allow for method chaining. * @example * map.setFog({ @@ -3098,7 +3348,7 @@ class Map extends Camera { * _Note: You can use the [`feature-state` expression](https://docs.mapbox.com/mapbox-gl-js/style-spec/expressions/#feature-state) to access the values in a feature's state object for the purposes of styling_. * * @param {Object} feature Feature identifier. Feature objects returned from - * {@link Map#queryRenderedFeatures} or event handlers can be used as feature identifiers. + * {@link Map#queryRenderedFeatures} or event handlers can be used as feature identifiers. * @param {number | string} feature.id Unique id of the feature. Can be an integer or a string, but supports string values only when the [`promoteId`](https://docs.mapbox.com/mapbox-gl-js/style-spec/sources/#vector-promoteId) option has been applied to the source or the string can be cast to an integer. * @param {string} feature.source The id of the vector or GeoJSON source for the feature. * @param {string} [feature.sourceLayer] (optional) *For vector tile sources, `sourceLayer` is required*. @@ -3140,7 +3390,7 @@ class Map extends Camera { * Features are identified by their `feature.id` attribute, which can be any number or string. * * @param {Object} feature Identifier of where to remove state. It can be a source, a feature, or a specific key of feature. - * Feature objects returned from {@link Map#queryRenderedFeatures} or event handlers can be used as feature identifiers. + * Feature objects returned from {@link Map#queryRenderedFeatures} or event handlers can be used as feature identifiers. * @param {number | string} [feature.id] (optional) Unique id of the feature. Can be an integer or a string, but supports string values only when the [`promoteId`](https://docs.mapbox.com/mapbox-gl-js/style-spec/sources/#vector-promoteId) option has been applied to the source or the string can be cast to an integer. * @param {string} feature.source The id of the vector or GeoJSON source for the feature. * @param {string} [feature.sourceLayer] (optional) For vector tile sources, `sourceLayer` is required. @@ -3194,7 +3444,7 @@ class Map extends Camera { * _Note: To access the values in a feature's state object for the purposes of styling the feature, use the [`feature-state` expression](https://docs.mapbox.com/mapbox-gl-js/style-spec/expressions/#feature-state)_. * * @param {Object} feature Feature identifier. Feature objects returned from - * {@link Map#queryRenderedFeatures} or event handlers can be used as feature identifiers. + * {@link Map#queryRenderedFeatures} or event handlers can be used as feature identifiers. * @param {number | string} feature.id Unique id of the feature. Can be an integer or a string, but supports string values only when the [`promoteId`](https://docs.mapbox.com/mapbox-gl-js/style-spec/sources/#vector-promoteId) option has been applied to the source or the string can be cast to an integer. * @param {string} feature.source The id of the vector or GeoJSON source for the feature. * @param {string} [feature.sourceLayer] (optional) *For vector tile sources, `sourceLayer` is required*. @@ -3342,7 +3592,7 @@ class Map extends Camera { storeAuthState(gl, true); - this.painter = new Painter(gl, this.transform); + this.painter = new Painter(gl, this._contextCreateOptions, this.transform, this._tp); this.on('data', (event: MapDataEvent) => { if (event.dataType === 'source') { this.painter.setTileLoadedFlag(true); @@ -3474,8 +3724,8 @@ class Map extends Camera { this.painter.setBaseState(); if (this.isMoving() || this.isRotating() || this.isZooming()) { - this._interactionRange[0] = Math.min(this._interactionRange[0], window.performance.now()); - this._interactionRange[1] = Math.max(this._interactionRange[1], window.performance.now()); + this._interactionRange[0] = Math.min(this._interactionRange[0], performance.now()); + this._interactionRange[1] = Math.max(this._interactionRange[1], performance.now()); } this._renderTaskQueue.run(paintStartTimeStamp); @@ -3519,7 +3769,7 @@ class Map extends Camera { if (this.style && this._sourcesDirty) { this._sourcesDirty = false; this.painter._updateFog(this.style); - this._updateTerrain(); // Terrain DEM source updates here and skips update in style._updateSources. + this._updateTerrain(); // Terrain DEM source updates here and skips update in Style#updateSources. averageElevationChanged = this._updateAverageElevation(frameStartTime); this.style.updateSources(this.transform); // Update positions of markers and popups on enabling/disabling terrain @@ -3534,6 +3784,7 @@ class Map extends Camera { if (this.style) { this.painter.render(this.style, { showTileBoundaries: this.showTileBoundaries, + showParseStatus: this.showParseStatus, wireframe: { terrain: this.showTerrainWireframe, layers2D: this.showLayers2DWireframe, @@ -3583,7 +3834,8 @@ class Map extends Camera { cpuTime: renderCPUTime, gpuTime: renderGPUTime })); - window.performance.mark('frame-gpu', { + // $FlowFixMe extra-arg: fixed in later Flow versions + performance.mark('frame-gpu', { startTime: frameStartTime, detail: { gpuTime: renderGPUTime @@ -3621,7 +3873,7 @@ class Map extends Camera { // // Even though `_styleDirty` and `_sourcesDirty` are reset in this // method, synchronous events fired during Style#update or - // Style#_updateSources could have caused them to be set again. + // Style#updateSources could have caused them to be set again. const somethingDirty = this._sourcesDirty || this._styleDirty || this._placementDirty || averageElevationChanged; if (somethingDirty || this._repaint) { this.triggerRepaint(); @@ -3779,12 +4031,26 @@ class Map extends Camera { } } }); + postMapLoadEvent(this._getMapId(), this._requestManager._skuToken, this._requestManager._customAccessToken, () => {}); } /***** END WARNING - REMOVAL OR MODIFICATION OF THE PRECEDING CODE VIOLATES THE MAPBOX TERMS OF SERVICE ******/ + _postStyleLoadEvent() { + if (!this.style.globalId) { + return; + } + + postStyleLoadEvent(this._requestManager._customAccessToken, { + map: this, + skuToken: this._requestManager._skuToken, + style: this.style.globalId, + importedStyles: this.style.getImportGlobalIds() + }); + } + _updateTerrain() { // Recalculate if enabled/disabled and calculate elevation cover. As camera is using elevation tiles before // render (and deferred update after zoom recalculation), this needs to be called when removing terrain source. @@ -3866,18 +4132,16 @@ class Map extends Camera { this.handlers = undefined; this.setStyle(null); - if (typeof window !== 'undefined') { - // $FlowFixMe[method-unbinding] - window.removeEventListener('resize', this._onWindowResize, false); - // $FlowFixMe[method-unbinding] - window.removeEventListener('orientationchange', this._onWindowResize, false); - // $FlowFixMe[method-unbinding] - window.removeEventListener(this._fullscreenchangeEvent, this._onWindowResize, false); - // $FlowFixMe[method-unbinding] - window.removeEventListener('online', this._onWindowOnline, false); - // $FlowFixMe[method-unbinding] - window.removeEventListener('visibilitychange', this._onVisibilityChange, false); - } + // $FlowFixMe[method-unbinding] + window.removeEventListener('resize', this._onWindowResize, false); + // $FlowFixMe[method-unbinding] + window.removeEventListener('orientationchange', this._onWindowResize, false); + // $FlowFixMe[method-unbinding] + window.removeEventListener(this._fullscreenchangeEvent, this._onWindowResize, false); + // $FlowFixMe[method-unbinding] + window.removeEventListener('online', this._onWindowOnline, false); + // $FlowFixMe[method-unbinding] + window.removeEventListener('visibilitychange', this._onVisibilityChange, false); const extension = this.painter.context.gl.getExtension('WEBGL_lose_context'); if (extension) extension.loseContext(); @@ -3902,6 +4166,10 @@ class Map extends Camera { PerformanceUtils.clearMetrics(); removeAuthState(this.painter.context.gl); + + mapSessionAPI.remove(); + mapLoadEvent.remove(); + this._removed = true; this.fire(new Event('remove')); } @@ -3944,8 +4212,8 @@ class Map extends Camera { */ // $FlowFixMe[method-unbinding] _preloadTiles(transform: Transform | Array): this { - const sources: Array = this.style ? (Object.values(this.style._sourceCaches): any) : []; - asyncAll(sources, (source, done) => source._preloadTiles(transform, done), () => { + const sourceCaches: Array = this.style ? this.style.getSourceCaches() : []; + asyncAll(sourceCaches, (sourceCache, done) => sourceCache._preloadTiles(transform, done), () => { this.triggerRepaint(); }); @@ -3963,7 +4231,7 @@ class Map extends Camera { } _onVisibilityChange() { - if (window.document.visibilityState === 'hidden') { + if (document.visibilityState === 'hidden') { this._visibilityHidden++; } } @@ -3972,11 +4240,7 @@ class Map extends Camera { /** * Gets and sets a Boolean indicating whether the map will render an outline - * around each tile and the tile ID. These tile boundaries are useful for - * debugging. - * - * The uncompressed file size of the first vector source is drawn in the top left - * corner of each tile, next to the tile ID. + * around each tile. These tile boundaries are useful for debugging. * * @name showTileBoundaries * @type {boolean} @@ -3989,6 +4253,29 @@ class Map extends Camera { set showTileBoundaries(value: boolean) { if (this._showTileBoundaries === value) return; this._showTileBoundaries = value; + this._tp.refreshUI(); + this._update(); + } + + /** + * Gets and sets a Boolean indicating whether the map will render the tile ID + * and the status of the tile in their corner when `showTileBoundaries` is on. + * + * The uncompressed file size of the first vector source is drawn in the top left + * corner of each tile, next to the tile ID. + * + * @name showParseStatus + * @type {boolean} + * @instance + * @memberof Map + * @example + * map.showParseStatus = true; + */ + get showParseStatus(): boolean { return !!this._showParseStatus; } + set showParseStatus(value: boolean) { + if (this._showParseStatus === value) return; + this._showParseStatus = value; + this._tp.refreshUI(); this._update(); } @@ -4009,6 +4296,7 @@ class Map extends Camera { set showTerrainWireframe(value: boolean) { if (this._showTerrainWireframe === value) return; this._showTerrainWireframe = value; + this._tp.refreshUI(); this._update(); } @@ -4029,6 +4317,7 @@ class Map extends Camera { set showLayers2DWireframe(value: boolean) { if (this._showLayers2DWireframe === value) return; this._showLayers2DWireframe = value; + this._tp.refreshUI(); this._update(); } @@ -4049,6 +4338,7 @@ class Map extends Camera { set showLayers3DWireframe(value: boolean) { if (this._showLayers3DWireframe === value) return; this._showLayers3DWireframe = value; + this._tp.refreshUI(); this._update(); } @@ -4083,6 +4373,7 @@ class Map extends Camera { set showPadding(value: boolean) { if (this._showPadding === value) return; this._showPadding = value; + this._tp.refreshUI(); this._update(); } @@ -4101,6 +4392,7 @@ class Map extends Camera { set showCollisionBoxes(value: boolean) { if (this._showCollisionBoxes === value) return; this._showCollisionBoxes = value; + this._tp.refreshUI(); if (value) { // When we turn collision boxes on we have to generate them for existing tiles // When we turn them off, there's no cost to leaving existing boxes in place @@ -4127,6 +4419,7 @@ class Map extends Camera { set showOverdrawInspector(value: boolean) { if (this._showOverdrawInspector === value) return; this._showOverdrawInspector = value; + this._tp.refreshUI(); this._update(); } @@ -4143,6 +4436,7 @@ class Map extends Camera { set repaint(value: boolean) { if (this._repaint !== value) { this._repaint = value; + this._tp.refreshUI(); this.triggerRepaint(); } } @@ -4160,6 +4454,7 @@ class Map extends Camera { set showTileAABBs(value: boolean) { if (this._showTileAABBs === value) return; this._showTileAABBs = value; + this._tp.refreshUI(); if (!value) { Debug.clearAabbs(); return; } this._update(); } @@ -4181,8 +4476,6 @@ class Map extends Camera { get version(): string { return version; } } -export default Map; - /** * Interface for interactive controls added to the map. This is a * specification for implementers to model: it is not @@ -4240,9 +4533,9 @@ export default Map; * @name onAdd * @param {Map} map The Map this control will be added to. * @returns {HTMLElement} The control's container element. This should - * be created by the control and returned by onAdd without being attached - * to the DOM: the map will insert the control's element into the DOM - * as necessary. + * be created by the control and returned by onAdd without being attached + * to the DOM: the map will insert the control's element into the DOM + * as necessary. */ /** diff --git a/src/ui/marker.js b/src/ui/marker.js index b15ba3d3376..bd42fe4af9e 100644 --- a/src/ui/marker.js +++ b/src/ui/marker.js @@ -1,19 +1,19 @@ // @flow import * as DOM from '../util/dom.js'; -import window from '../util/window.js'; import LngLat from '../geo/lng_lat.js'; import Point from '@mapbox/point-geometry'; import smartWrap from '../util/smart_wrap.js'; import {bindAll, extend, radToDeg, smoothstep} from '../util/util.js'; import {type Anchor, anchorTranslate} from './anchor.js'; import {Event, Evented} from '../util/evented.js'; -import type Map from './map.js'; +import type {Map} from './map.js'; import type Popup from './popup.js'; import type {LngLatLike} from "../geo/lng_lat.js"; import type {MapMouseEvent, MapTouchEvent} from './events.js'; -import type {PointLike} from '@mapbox/point-geometry'; -import {globeTiltAtLngLat, globeCenterToScreenPoint, isLngLatBehindGlobe, GLOBE_ZOOM_THRESHOLD_MAX} from '../geo/projection/globe_util.js'; +import type {PointLike} from '../types/point-like.js'; +import {GLOBE_ZOOM_THRESHOLD_MAX} from '../geo/projection/globe_constants.js'; +import {globeTiltAtLngLat, globeCenterToScreenPoint, isLngLatBehindGlobe} from '../geo/projection/globe_util.js'; import assert from 'assert'; type Options = { @@ -37,7 +37,7 @@ type Options = { * @param {Object} [options] * @param {HTMLElement} [options.element] DOM element to use as a marker. The default is a light blue, droplet-shaped SVG marker. * @param {string} [options.anchor='center'] A string indicating the part of the Marker that should be positioned closest to the coordinate set via {@link Marker#setLngLat}. - * Options are `'center'`, `'top'`, `'bottom'`, `'left'`, `'right'`, `'top-left'`, `'top-right'`, `'bottom-left'`, and `'bottom-right'`. + * Options are `'center'`, `'top'`, `'bottom'`, `'left'`, `'right'`, `'top-left'`, `'top-right'`, `'bottom-left'`, and `'bottom-right'`. * @param {PointLike} [options.offset] The offset in pixels as a {@link PointLike} object to apply relative to the element's center. Negatives indicate left and up. * @param {string} [options.color='#3FB1CE'] The color to use for the default marker if `options.element` is not provided. The default is light blue. * @param {number} [options.scale=1] The scale to use for the default marker if `options.element` is not provided. The default scale corresponds to a height of `41px` and a width of `27px`. @@ -85,7 +85,7 @@ export default class Marker extends Evented { _rotationAlignment: string; _originalTabIndex: ?string; // original tabindex of _element _fadeTimer: ?TimeoutID; - _updateFrameId: number; + _updateFrameId: AnimationFrameID; _updateMoving: () => void; _occludedOpacity: number; @@ -93,7 +93,7 @@ export default class Marker extends Evented { super(); // For backward compatibility -- the constructor used to accept the element as a // required first argument, before it was made optional. - if (options instanceof window.HTMLElement || legacyOptions) { + if (options instanceof HTMLElement || legacyOptions) { options = extend({element: options}, legacyOptions); } @@ -322,7 +322,7 @@ export default class Marker extends Evented { * Binds a {@link Popup} to the {@link Marker}. * * @param {Popup | null} popup An instance of the {@link Popup} class. If undefined or null, any popup - * set on this {@link Marker} instance is unset. + * set on this {@link Marker} instance is unset. * @returns {Marker} Returns itself to allow for method chaining. * @example * const marker = new mapboxgl.Marker() @@ -571,7 +571,7 @@ export default class Marker extends Evented { } _update(delaySnap?: boolean) { - window.cancelAnimationFrame(this._updateFrameId); + cancelAnimationFrame(this._updateFrameId); const map = this._map; if (!map) return; @@ -585,7 +585,7 @@ export default class Marker extends Evented { // we only round them when _update is called with `moveend` or when its called with // no arguments (when the Marker is initialized or Marker#setLngLat is invoked). if (delaySnap === true) { - this._updateFrameId = window.requestAnimationFrame(() => { + this._updateFrameId = requestAnimationFrame(() => { if (this._element && this._pos && this._anchor) { this._pos = this._pos.round(); this._updateDOM(); diff --git a/src/ui/popup.js b/src/ui/popup.js index 39fcc2ad979..cde467c4137 100644 --- a/src/ui/popup.js +++ b/src/ui/popup.js @@ -6,14 +6,13 @@ import {MapMouseEvent} from '../ui/events.js'; import * as DOM from '../util/dom.js'; import LngLat from '../geo/lng_lat.js'; import Point from '@mapbox/point-geometry'; -import window from '../util/window.js'; import smartWrap from '../util/smart_wrap.js'; import {type Anchor, anchorTranslate} from './anchor.js'; import {isLngLatBehindGlobe} from '../geo/projection/globe_util.js'; -import type Map from './map.js'; +import type {Map} from './map.js'; import type {LngLatLike} from '../geo/lng_lat.js'; -import type {PointLike} from '@mapbox/point-geometry'; +import type {PointLike} from '../types/point-like.js'; import type Marker from './marker.js'; const defaultOptions = { @@ -52,31 +51,31 @@ const focusQuerySelector = [ * * @param {Object} [options] * @param {boolean} [options.closeButton=true] If `true`, a close button will appear in the - * top right corner of the popup. + * top right corner of the popup. * @param {boolean} [options.closeOnClick=true] If `true`, the popup will close when the - * map is clicked. + * map is clicked. * @param {boolean} [options.closeOnMove=false] If `true`, the popup will close when the - * map moves. + * map moves. * @param {boolean} [options.focusAfterOpen=true] If `true`, the popup will try to focus the - * first focusable element inside the popup. + * first focusable element inside the popup. * @param {string} [options.anchor] - A string indicating the part of the popup that should - * be positioned closest to the coordinate, set via {@link Popup#setLngLat}. - * Options are `'center'`, `'top'`, `'bottom'`, `'left'`, `'right'`, `'top-left'`, - * `'top-right'`, `'bottom-left'`, and `'bottom-right'`. If unset, the anchor will be - * dynamically set to ensure the popup falls within the map container with a preference - * for `'bottom'`. + * be positioned closest to the coordinate, set via {@link Popup#setLngLat}. + * Options are `'center'`, `'top'`, `'bottom'`, `'left'`, `'right'`, `'top-left'`, + * `'top-right'`, `'bottom-left'`, and `'bottom-right'`. If unset, the anchor will be + * dynamically set to ensure the popup falls within the map container with a preference + * for `'bottom'`. * @param {number | PointLike | Object} [options.offset] - - * A pixel offset applied to the popup's location specified as: - * - a single number specifying a distance from the popup's location - * - a {@link PointLike} specifying a constant offset - * - an object of {@link Point}s specifing an offset for each anchor position. + * A pixel offset applied to the popup's location specified as: + * - a single number specifying a distance from the popup's location + * - a {@link PointLike} specifying a constant offset + * - an object of {@link Point}s specifing an offset for each anchor position. * - * Negative offsets indicate left and up. + * Negative offsets indicate left and up. * @param {string} [options.className] Space-separated CSS class names to add to popup container. * @param {string} [options.maxWidth='240px'] - - * A string that sets the CSS property of the popup's maximum width (for example, `'300px'`). - * To ensure the popup resizes to fit its content, set this property to `'none'`. - * See the MDN documentation for the list of [available values](https://developer.mozilla.org/en-US/docs/Web/CSS/max-width). + * A string that sets the CSS property of the popup's maximum width (for example, `'300px'`). + * To ensure the popup resizes to fit its content, set this property to `'none'`. + * See the MDN documentation for the list of [available values](https://developer.mozilla.org/en-US/docs/Web/CSS/max-width). * @example * const markerHeight = 50; * const markerRadius = 10; @@ -115,7 +114,7 @@ export default class Popup extends Evented { _classList: Set; _marker: ?Marker; - constructor(options: PopupOptions) { + constructor(options?: PopupOptions) { super(); this.options = extend(Object.create(defaultOptions), options); bindAll(['_update', '_onClose', 'remove', '_onMouseEvent'], this); @@ -375,7 +374,7 @@ export default class Popup extends Evented { * .addTo(map); */ setText(text: string): this { - return this.setDOMContent(window.document.createTextNode(text)); + return this.setDOMContent(document.createTextNode(text)); } /** @@ -398,8 +397,8 @@ export default class Popup extends Evented { * @see [Example: Attach a popup to a marker instance](https://docs.mapbox.com/mapbox-gl-js/example/set-popup/) */ setHTML(html: string): this { - const frag = window.document.createDocumentFragment(); - const temp = window.document.createElement('body'); + const frag = document.createDocumentFragment(); + const temp = document.createElement('body'); let child; temp.innerHTML = html; while (true) { diff --git a/src/util/actor.js b/src/util/actor.js index 12c066d931a..0f10ab51593 100644 --- a/src/util/actor.js +++ b/src/util/actor.js @@ -113,7 +113,8 @@ class Actor { // We're using a MessageChannel object to get throttle the process() flow to one at a time. const callback = this.callbacks[id]; const metadata = (callback && callback.metadata) || {type: "message"}; - this.cancelCallbacks[id] = this.scheduler.add(() => this.processTask(id, data), metadata); + const cancel = this.scheduler.add(() => this.processTask(id, data), metadata); + if (cancel) this.cancelCallbacks[id] = cancel; } else { // In the main thread, process messages immediately so that other work does not slip in // between getting partial data back from workers. @@ -123,6 +124,8 @@ class Actor { } processTask(id: number, task: any) { + // Always delete since we are no longer cancellable + delete this.cancelCallbacks[id]; if (task.type === '') { // The done() function in the counterpart has been called, and we are now // firing the callback in the originating actor, if there is one. @@ -139,7 +142,6 @@ class Actor { } else { const buffers: Set = new Set(); const done = task.hasCallback ? (err: ?Error, data: mixed) => { - delete this.cancelCallbacks[id]; this.target.postMessage({ id, type: '', diff --git a/src/util/ajax.js b/src/util/ajax.js index 7319d0f0f0a..3286c261617 100644 --- a/src/util/ajax.js +++ b/src/util/ajax.js @@ -1,8 +1,7 @@ // @flow -import window from './window.js'; import {extend, warnOnce, isWorker} from './util.js'; -import {isMapboxHTTPURL, hasCacheDefeatingSku} from './mapbox.js'; +import {isMapboxHTTPURL, hasCacheDefeatingSku} from './mapbox_url.js'; import config from './config.js'; import assert from 'assert'; import {cacheGet, cachePut} from './tile_request_cache.js'; @@ -96,10 +95,9 @@ export class AJAXError extends Error { // For files loaded from the local file system, `location.origin` will be set // to the string(!) "null" (Firefox), or "file://" (Chrome, Safari, Edge, IE), // and we will set an empty referrer. Otherwise, we're using the document's URL. -/* global self */ export const getReferrer: (() => string) = isWorker() ? () => self.worker && self.worker.referrer : - () => (window.location.protocol === 'blob:' ? window.parent : window).location.href; + () => (location.protocol === 'blob:' ? parent : self).location.href; // Determines whether a URL is a file:// URL. This is obviously the case if it begins // with file://. Relative URLs are also file:// URLs iff the original document was loaded @@ -107,8 +105,8 @@ export const getReferrer: (() => string) = isWorker() ? const isFileURL = (url: string) => /^file:/.test(url) || (/^file:/.test(getReferrer()) && !/^\w+:/.test(url)); function makeFetchRequest(requestParameters: RequestParameters, callback: ResponseCallback): Cancelable { - const controller = new window.AbortController(); - const request = new window.Request(requestParameters.url, { + const controller = new AbortController(); + const request = new Request(requestParameters.url, { method: requestParameters.method || 'GET', body: requestParameters.body, credentials: requestParameters.credentials, @@ -148,7 +146,7 @@ function makeFetchRequest(requestParameters: RequestParameters, callback: Respon const requestTime = Date.now(); - window.fetch(request).then(response => { + fetch(request).then(response => { if (response.ok) { const cacheableResponse = cacheIgnoringSearch ? response.clone() : null; return finishRequest(response, cacheableResponse, requestTime); @@ -199,7 +197,7 @@ function makeFetchRequest(requestParameters: RequestParameters, callback: Respon } function makeXMLHttpRequest(requestParameters: RequestParameters, callback: ResponseCallback): Cancelable { - const xhr: XMLHttpRequest = new window.XMLHttpRequest(); + const xhr: XMLHttpRequest = new XMLHttpRequest(); xhr.open(requestParameters.method || 'GET', requestParameters.url, true); if (requestParameters.type === 'arrayBuffer') { xhr.responseType = 'arraybuffer'; @@ -237,13 +235,13 @@ function makeXMLHttpRequest(requestParameters: RequestParameters, callback: Resp export const makeRequest = function(requestParameters: RequestParameters, callback: ResponseCallback): Cancelable { // We're trying to use the Fetch API if possible. However, in some situations we can't use it: - // - Safari exposes window.AbortController, but it doesn't work actually abort any requests in + // - Safari exposes AbortController, but it doesn't work actually abort any requests in // older versions (see https://bugs.webkit.org/show_bug.cgi?id=174980#c2). In this case, // we dispatch the request to the main thread so that we can get an accurate referrer header. // - Requests for resources with the file:// URI scheme don't work with the Fetch API either. In // this case we unconditionally use XHR on the current thread since referrers don't matter. if (!isFileURL(requestParameters.url)) { - if (window.fetch && window.Request && window.AbortController && window.Request.prototype.hasOwnProperty('signal')) { + if (self.fetch && self.Request && self.AbortController && Request.prototype.hasOwnProperty('signal')) { return makeFetchRequest(requestParameters, callback); } if (isWorker() && self.worker && self.worker.actor) { @@ -271,16 +269,15 @@ export const getData = function(requestParameters: RequestParameters, callback: }; function sameOrigin(url: string) { - const a: HTMLAnchorElement = window.document.createElement('a'); + const a: HTMLAnchorElement = document.createElement('a'); a.href = url; - return a.protocol === window.document.location.protocol && a.host === window.document.location.host; + return a.protocol === location.protocol && a.host === location.host; } const transparentPngUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQYV2NgAAIAAAUAAarVyFEAAAAASUVORK5CYII='; function arrayBufferToImage(data: ArrayBuffer, callback: Callback) { - const img: HTMLImageElement = new window.Image(); - const URL = window.URL; + const img: HTMLImageElement = new Image(); img.onload = () => { callback(null, img); URL.revokeObjectURL(img.src); @@ -288,16 +285,17 @@ function arrayBufferToImage(data: ArrayBuffer, callback: Callback { img.src = transparentPngUrl; }); + requestAnimationFrame(() => { img.src = transparentPngUrl; }); }; img.onerror = () => callback(new Error('Could not load image. Please make sure to use a supported image type such as PNG or JPEG. Note that SVGs are not supported.')); - const blob: Blob = new window.Blob([new Uint8Array(data)], {type: 'image/png'}); + const blob: Blob = new Blob([new Uint8Array(data)], {type: 'image/png'}); img.src = data.byteLength ? URL.createObjectURL(blob) : transparentPngUrl; } function arrayBufferToImageBitmap(data: ArrayBuffer, callback: Callback) { - const blob: Blob = new window.Blob([new Uint8Array(data)], {type: 'image/png'}); - window.createImageBitmap(blob).then((imgBitmap) => { + const blob: Blob = new Blob([new Uint8Array(data)], {type: 'image/png'}); + // $FlowFixMe[cannot-resolve-name] https://github.com/facebook/flow/pull/7483 + createImageBitmap(blob).then((imgBitmap) => { callback(null, imgBitmap); }).catch((e) => { callback(new Error(`Could not load image because of ${e.message}. Please make sure to use a supported image type such as PNG or JPEG. Note that SVGs are not supported.`)); @@ -358,7 +356,7 @@ export const getImage = function(requestParameters: RequestParameters, callback: if (err) { callback(err); } else if (data) { - if (window.createImageBitmap) { + if (self.createImageBitmap) { arrayBufferToImageBitmap(data, (err, imgBitmap) => callback(err, imgBitmap, cacheControl, expires)); } else { arrayBufferToImage(data, (err, img) => callback(err, img, cacheControl, expires)); @@ -375,13 +373,13 @@ export const getImage = function(requestParameters: RequestParameters, callback: }; export const getVideo = function(urls: Array, callback: Callback): Cancelable { - const video: HTMLVideoElement = window.document.createElement('video'); + const video: HTMLVideoElement = document.createElement('video'); video.muted = true; video.onloadstart = function() { callback(null, video); }; for (let i = 0; i < urls.length; i++) { - const s: HTMLSourceElement = window.document.createElement('source'); + const s: HTMLSourceElement = document.createElement('source'); if (!sameOrigin(urls[i])) { video.crossOrigin = 'Anonymous'; } diff --git a/src/util/browser.js b/src/util/browser.js index 4a0a70e903e..55d0a821c45 100755 --- a/src/util/browser.js +++ b/src/util/browser.js @@ -1,6 +1,6 @@ // @flow strict - -import window from './window.js'; +import assert from 'assert'; +import offscreenCanvasSupported from './offscreen_canvas_supported.js'; import type {Cancelable} from '../types/cancelable.js'; let linkEl; @@ -11,6 +11,8 @@ let stubTime: number | void; let canvas; +let hasCanvasFingerprintNoise; + /** * @private */ @@ -23,7 +25,7 @@ const exported = { if (stubTime !== undefined) { return stubTime; } - return window.performance.now(); + return performance.now(); }, setNow(time: number) { stubTime = time; @@ -34,15 +36,15 @@ const exported = { }, frame(fn: (paintStartTimestamp: number) => void): Cancelable { - const frame = window.requestAnimationFrame(fn); - return {cancel: () => window.cancelAnimationFrame(frame)}; + const frame = requestAnimationFrame(fn); + return {cancel: () => cancelAnimationFrame(frame)}; }, getImageData(img: CanvasImageSource, padding?: number = 0): ImageData { const {width, height} = img; if (!canvas) { - canvas = window.document.createElement('canvas'); + canvas = document.createElement('canvas'); } const context = canvas.getContext('2d', {willReadFrequently: true}); @@ -61,7 +63,7 @@ const exported = { }, resolveURL(path: string): string { - if (!linkEl) linkEl = window.document.createElement('a'); + if (!linkEl) linkEl = document.createElement('a'); linkEl.href = path; return linkEl.href; }, @@ -75,6 +77,43 @@ const exported = { } return reducedMotionQuery.matches; }, + + /** + * Returns true if the browser has OffscreenCanvas support and + * adds noise to Canvas2D operations used for image decoding to prevent fingerprinting. + */ + hasCanvasFingerprintNoise(): boolean { + if (hasCanvasFingerprintNoise !== undefined) { + return hasCanvasFingerprintNoise; + } + + if (!offscreenCanvasSupported()) { + hasCanvasFingerprintNoise = false; + return false; + } + + assert(self.OffscreenCanvas, 'OffscreenCanvas is not supported'); + + const offscreenCanvas = new OffscreenCanvas(255 / 3, 1); + // $FlowFixMe[extra-arg] probably fixed in later versions of Flow + const offscreenCanvasContext = offscreenCanvas.getContext('2d', {willReadFrequently: true}); + let inc = 0; + // getImageData is lossy with premultiplied alpha. + for (let i = 0; i < offscreenCanvas.width; ++i) { + offscreenCanvasContext.fillStyle = `rgba(${inc++},${inc++},${inc++}, 255)`; + offscreenCanvasContext.fillRect(i, 0, 1, 1); + } + const readData = offscreenCanvasContext.getImageData(0, 0, offscreenCanvas.width, offscreenCanvas.height); + inc = 0; + for (let i = 0; i < readData.data.length; ++i) { + if (i % 4 !== 3 && inc++ !== readData.data[i]) { + hasCanvasFingerprintNoise = true; + return true; + } + } + hasCanvasFingerprintNoise = false; + return false; + } }; export default exported; diff --git a/src/util/browser/web_worker.js b/src/util/browser/web_worker.js deleted file mode 100644 index cd1273262f4..00000000000 --- a/src/util/browser/web_worker.js +++ /dev/null @@ -1,10 +0,0 @@ -// @flow - -import window from '../window.js'; -import mapboxgl from '../../index.js'; - -import type {WorkerInterface} from '../web_worker.js'; - -export default function (): WorkerInterface { - return (mapboxgl.workerClass != null) ? new mapboxgl.workerClass() : (new window.Worker(mapboxgl.workerUrl): any); // eslint-disable-line new-cap -} diff --git a/src/util/browser/window.js b/src/util/browser/window.js deleted file mode 100644 index 82c015d3a56..00000000000 --- a/src/util/browser/window.js +++ /dev/null @@ -1,6 +0,0 @@ -// @flow -/* eslint-env browser */ -import type {Window} from '../../types/window.js'; - -// shim window for the case of requiring the browser bundle in Node -export default ((typeof self !== 'undefined' ? self : ({}: any)): Window); diff --git a/src/util/config.js b/src/util/config.js index d103616e961..5615079c95c 100644 --- a/src/util/config.js +++ b/src/util/config.js @@ -14,9 +14,12 @@ type Config = {| REQUIRE_ACCESS_TOKEN: boolean, TILE_URL_VERSION: string, RASTER_URL_PREFIX: string, + RASTERARRAYS_URL_PREFIX: string, ACCESS_TOKEN: ?string, MAX_PARALLEL_IMAGE_REQUESTS: number, DRACO_URL: string, + MESHOPT_URL: string, + MESHOPT_SIMD_URL: string, DEFAULT_STYLE: string, GLYPHS_URL: string, |}; @@ -75,11 +78,14 @@ const config: Config = { FEEDBACK_URL: 'https://apps.mapbox.com/feedback', TILE_URL_VERSION: 'v4', RASTER_URL_PREFIX: 'raster/v1', + RASTERARRAYS_URL_PREFIX: 'rasterarrays/v1', REQUIRE_ACCESS_TOKEN: true, ACCESS_TOKEN: null, DEFAULT_STYLE: 'mapbox://styles/mapbox/standard', MAX_PARALLEL_IMAGE_REQUESTS: 16, DRACO_URL: 'https://api.mapbox.com/mapbox-gl-js/draco_decoder_gltf_v1.5.6.wasm', + MESHOPT_URL: 'https://api.mapbox.com/mapbox-gl-js/meshopt_base_v0.20.wasm', + MESHOPT_SIMD_URL: 'https://api.mapbox.com/mapbox-gl-js/meshopt_simd_v0.20.wasm', GLYPHS_URL: 'mapbox://fonts/mapbox/{fontstack}/{range}.pbf' }; diff --git a/src/util/debug.js b/src/util/debug.js index 070a0a3692f..33bd232b4c5 100644 --- a/src/util/debug.js +++ b/src/util/debug.js @@ -1,6 +1,5 @@ // @flow import {extend} from './util.js'; -import window from './window.js'; import assert from 'assert'; import {mat4, vec3} from 'gl-matrix'; import {aabbForTileOnGlobe} from '../geo/projection/globe_util.js'; @@ -39,7 +38,7 @@ export const Debug: { }, logToElement(message: string, overwrite: boolean = false, id: string = "log") { - const el = window.document.getElementById(id); + const el = document.getElementById(id); if (el) { if (overwrite) el.innerHTML = ''; el.innerHTML += `
${message}`; @@ -52,20 +51,17 @@ export const Debug: { _initializeCanvas(tr: Transform) { if (!Debug.debugCanvas) { - Debug.debugCanvas = window.document.createElement('canvas'); - window.document.body.appendChild(Debug.debugCanvas); - // Supress Flow check because we're checking for null above - if (!Debug.debugCanvas) return; + const canvas = Debug.debugCanvas = document.createElement('canvas'); + if (document.body) document.body.appendChild(canvas); - Debug.debugCanvas.style.position = 'absolute'; - Debug.debugCanvas.style.left = '0'; - Debug.debugCanvas.style.top = '0'; - Debug.debugCanvas.style.pointerEvents = 'none'; + canvas.style.position = 'absolute'; + canvas.style.left = '0'; + canvas.style.top = '0'; + canvas.style.pointerEvents = 'none'; const resize = () => { - if (!Debug.debugCanvas) { return; } - Debug.debugCanvas.width = tr.width; - Debug.debugCanvas.height = tr.height; + canvas.width = tr.width; + canvas.height = tr.height; }; resize(); diff --git a/src/util/dispatcher.js b/src/util/dispatcher.js index 50c6e118f3e..446fe20181f 100644 --- a/src/util/dispatcher.js +++ b/src/util/dispatcher.js @@ -5,6 +5,7 @@ import Actor from './actor.js'; import assert from 'assert'; import type WorkerPool from './worker_pool.js'; +import type {Class} from '../types/class.js'; /** * Responsible for sending messages from a {@link Source} to an associated diff --git a/src/util/dom.js b/src/util/dom.js index ac5086413bf..04cb9dda58f 100644 --- a/src/util/dom.js +++ b/src/util/dom.js @@ -1,41 +1,42 @@ // @flow strict import Point from '@mapbox/point-geometry'; - -import window from './window.js'; import assert from 'assert'; // refine the return type based on tagName, e.g. 'button' -> HTMLButtonElement // $FlowFixMe[method-unbinding] export function create(tagName: T, className: ?string, container?: HTMLElement): $Call { - const el = window.document.createElement(tagName); - if (className !== undefined) el.className = className; + const el = document.createElement(tagName); + if (className !== undefined && className !== null) el.className = className; if (container) container.appendChild(el); return el; } export function createSVG(tagName: string, attributes: {[string]: string | number}, container?: Element): Element { - const el = window.document.createElementNS('http://www.w3.org/2000/svg', tagName); + const el = document.createElementNS('http://www.w3.org/2000/svg', tagName); for (const name of Object.keys(attributes)) { - el.setAttributeNS(null, name, attributes[name]); + el.setAttributeNS(null, name, String(attributes[name])); } if (container) container.appendChild(el); return el; } -const docStyle = window.document && window.document.documentElement.style; +const docStyle = typeof document !== 'undefined' ? document.documentElement && document.documentElement.style : null; const selectProp = docStyle && docStyle.userSelect !== undefined ? 'userSelect' : 'WebkitUserSelect'; let userSelect; export function disableDrag() { if (docStyle && selectProp) { + // $FlowFixMe[incompatible-type] userSelect = docStyle[selectProp]; + // $FlowFixMe[incompatible-type] docStyle[selectProp] = 'none'; } } export function enableDrag() { if (docStyle && selectProp) { + // $FlowFixMe[incompatible-type] docStyle[selectProp] = userSelect; } } diff --git a/src/util/evented.js b/src/util/evented.js index 19ec3dcd154..302698e247d 100644 --- a/src/util/evented.js +++ b/src/util/evented.js @@ -3,7 +3,7 @@ import {extend} from './util.js'; import type {MapEvent} from '../ui/events.js'; -type Listener = (Object) => any; +export type Listener = (Object) => void; type Listeners = {[_: string]: Array }; function _addEventListener(type: string, listener: Listener, listenerList: Listeners) { @@ -101,7 +101,7 @@ export class Evented { */ once(type: MapEvent, listener?: Listener): this | Promise { if (!listener) { - return new Promise(resolve => this.once(type, resolve)); + return new Promise((resolve) => this.once(type, resolve)); } this._oneTimeListeners = this._oneTimeListeners || {}; diff --git a/src/util/gl.js b/src/util/gl.js deleted file mode 100644 index 9374cd6108b..00000000000 --- a/src/util/gl.js +++ /dev/null @@ -1,33 +0,0 @@ -// @flow strict - -import gl from 'gl'; - -export default function(width: number, height: number, attributes: WebGLContextAttributes): WebGL2RenderingContext { - const context = gl(width, height, attributes); - - // Mock WebGL2 methods - context.createVertexArray = function() { return null; }; - context.deleteVertexArray = function() {}; - context.bindVertexArray = function() {}; - context.drawElementsInstanced = function() {}; - context.getBufferSubData = function() {}; - context.vertexAttribDivisor = function() {}; - - // Override WebGL2 methods to bypass asserts in src/render/program.js - - // $FlowFixMe[incompatible-use] - context.getShaderParameter = function() { return true; }; - - // $FlowFixMe[incompatible-use] - context.getProgramParameter = function() { return true; }; - - // $FlowFixMe[incompatible-use] - const getExtension = context.getExtension; - // $FlowFixMe[incompatible-use] - context.getExtension = function(extension) { - if (extension === 'OES_texture_float_linear') return undefined; - return getExtension(extension); - }; - - return context; -} diff --git a/src/util/image.js b/src/util/image.js index 496a01657cc..a3f71e7e389 100644 --- a/src/util/image.js +++ b/src/util/image.js @@ -51,7 +51,7 @@ function resizeImage(image: T, newImage: T, channels: image.data = newImage.data; } -function copyImage(srcImg: T | ImageData, dstImg: T, srcPt: Point, dstPt: Point, size: Size, channels: number): T { +function copyImage(srcImg: T | ImageData, dstImg: T, srcPt: Point, dstPt: Point, size: Size, channels: number, overrideRGBWithWhite: ?boolean): T { if (size.width === 0 || size.height === 0) { return dstImg; } @@ -72,14 +72,27 @@ function copyImage(srcImg: T | ImageData, dstImg: T, const srcData = srcImg.data; const dstData = dstImg.data; + const overrideRGB = channels === 4 && overrideRGBWithWhite; assert(srcData !== dstData); for (let y = 0; y < size.height; y++) { const srcOffset = ((srcPt.y + y) * srcImg.width + srcPt.x) * channels; const dstOffset = ((dstPt.y + y) * dstImg.width + dstPt.x) * channels; - for (let i = 0; i < size.width * channels; i++) { - dstData[dstOffset + i] = srcData[srcOffset + i]; + if (overrideRGB) { + for (let i = 0; i < size.width; i++) { + const srcByteOffset = srcOffset + i * channels + 3; + const dstPixelOffset = dstOffset + i * channels; + dstData[dstPixelOffset + 0] = 255; + dstData[dstPixelOffset + 1] = 255; + dstData[dstPixelOffset + 2] = 255; + dstData[dstPixelOffset + 3] = srcData[srcByteOffset]; + } + } else { + for (let i = 0; i < size.width * channels; i++) { + const srcByte = srcOffset + i; + dstData[dstOffset + i] = srcData[srcByte]; + } } } return dstImg; @@ -139,8 +152,8 @@ export class RGBAImage { return new RGBAImage({width: this.width, height: this.height}, new Uint8Array(this.data)); } - static copy(srcImg: RGBAImage | ImageData, dstImg: RGBAImage, srcPt: Point, dstPt: Point, size: Size) { - copyImage(srcImg, dstImg, srcPt, dstPt, size, 4); + static copy(srcImg: RGBAImage | ImageData, dstImg: RGBAImage, srcPt: Point, dstPt: Point, size: Size, overrideRGBWithWhite: ?boolean) { + copyImage(srcImg, dstImg, srcPt, dstPt, size, 4, overrideRGBWithWhite); } } diff --git a/src/util/live_performance.js b/src/util/live_performance.js index bd958e78356..91155ff9421 100644 --- a/src/util/live_performance.js +++ b/src/util/live_performance.js @@ -1,14 +1,13 @@ // @flow -import window from './window.js'; import {version as sdkVersion} from '../../package.json'; import { - isMapboxHTTPStyleURL, + isMapboxHTTPFontsURL, isMapboxHTTPTileJSONURL, isMapboxHTTPSpriteURL, - isMapboxHTTPFontsURL, + isMapboxHTTPStyleURL, isMapboxHTTPCDNURL -} from './mapbox.js'; +} from './mapbox_url.js'; type LivePerformanceMetrics = { counters: Array, @@ -37,10 +36,10 @@ export const PerformanceMarkers = { export const LivePerformanceUtils = { mark(marker: $Keys) { - window.performance.mark(marker); + performance.mark(marker); }, measure(name: string, begin?: string, end?: string) { - window.performance.measure(name, begin, end); + performance.measure(name, begin, end); } }; @@ -109,7 +108,7 @@ function getResourceCategory(entry: PerformanceResourceTiming): string { return 'other'; } -function getStyle(resourceTimers: Array): ?string { +function getStyle(resourceTimers: Array): ?string { if (resourceTimers) { for (const timer of resourceTimers) { const url = timer.name.split('?')[0]; @@ -124,13 +123,15 @@ function getStyle(resourceTimers: Array): ?string { } export function getLivePerformanceMetrics(data: LivePerformanceData): LivePerformanceMetrics { - const resourceTimers = window.performance.getEntriesByType('resource'); - const markerTimers = window.performance.getEntriesByType('mark'); + const resourceTimers = ((performance.getEntriesByType('resource'): any): Array); + const markerTimers = performance.getEntriesByType('mark'); const resourcesByType = categorize(resourceTimers, getResourceCategory); const counters = getCountersPerResourceType(resourcesByType); const devicePixelRatio = window.devicePixelRatio; - const connection = window.navigator.connection || window.navigator.mozConnection || window.navigator.webkitConnection; - const metrics = {counters: [], metadata: [], attributes: []}; + // $FlowFixMe[prop-missing] no connection types in Flow + const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection; + const effectiveType = connection ? (connection: any).effectiveType : undefined; + const metrics: LivePerformanceMetrics = {counters: [], metadata: [], attributes: []}; // Please read carefully before adding or modifying the following metrics: // https://github.com/mapbox/gl-js-team/blob/main/docs/live_performance_metrics.md @@ -165,8 +166,8 @@ export function getLivePerformanceMetrics(data: LivePerformanceData): LivePerfor addMetric(metrics.attributes, "zoom", data.zoom); addMetric(metrics.metadata, "devicePixelRatio", devicePixelRatio); - addMetric(metrics.metadata, "connectionEffectiveType", connection ? connection.effectiveType : undefined); - addMetric(metrics.metadata, "navigatorUserAgent", window.navigator.userAgent); + addMetric(metrics.metadata, "connectionEffectiveType", effectiveType); + addMetric(metrics.metadata, "navigatorUserAgent", navigator.userAgent); addMetric(metrics.metadata, "screenWidth", window.screen.width); addMetric(metrics.metadata, "screenHeight", window.screen.height); addMetric(metrics.metadata, "windowWidth", window.innerWidth); diff --git a/src/util/mapbox.js b/src/util/mapbox.js index fc605f7eb8f..dff1d645cd9 100644 --- a/src/util/mapbox.js +++ b/src/util/mapbox.js @@ -13,19 +13,21 @@ * and the Mapbox Terms of Service are available at https://www.mapbox.com/tos/ ******************************************************************************/ +import assert from 'assert'; import config from './config.js'; -import window from './window.js'; import webpSupported from './webp_supported.js'; +import {isMapboxHTTPURL, isMapboxURL} from './mapbox_url.js'; import {createSkuToken, SKU_ID} from './sku_token.js'; import {version as sdkVersion} from '../../package.json'; import {uuid, validateUuid, storageAvailable, b64DecodeUnicode, b64EncodeUnicode, warnOnce, extend} from './util.js'; import {postData, ResourceType, getData} from './ajax.js'; import {getLivePerformanceMetrics} from '../util/live_performance.js'; + import type {LivePerformanceData} from '../util/live_performance.js'; import type {RequestParameters} from './ajax.js'; import type {Cancelable} from '../types/cancelable.js'; import type {TileJSON} from '../types/tilejson.js'; -import assert from 'assert'; +import type {Map as MapboxMap} from "../ui/map"; type ResourceTypeEnum = $Keys; export type RequestTransformFunction = (url: string, resourceType?: ResourceTypeEnum) => RequestParameters; @@ -141,6 +143,8 @@ export class RequestManager { if (urlObject.authority === 'raster') { urlObject.path = `/${config.RASTER_URL_PREFIX}${urlObject.path}`; + } else if (urlObject.authority === 'rasterarrays') { + urlObject.path = `/${config.RASTERARRAYS_URL_PREFIX}${urlObject.path}`; } else { const tileURLAPIPrefixRe = /^.+\/v4\//; urlObject.path = urlObject.path.replace(tileURLAPIPrefixRe, '/'); @@ -161,8 +165,8 @@ export class RequestManager { const urlObject = parseUrl(url); // Make sure that we are dealing with a valid Mapbox tile URL. - // Has to begin with /v4/ or /raster/v1, with a valid filename + extension - if (!urlObject.path.match(/^(\/v4\/|\/raster\/v1\/)/) || !urlObject.path.match(extensionRe)) { + // Has to begin with /v4/, /raster/v1 or /rasterarrays/v1 with a valid filename + extension + if (!urlObject.path.match(/^(\/v4\/|\/(raster|rasterarrays)\/v1\/)/) || !urlObject.path.match(extensionRe)) { // Not a proper Mapbox tile URL. return url; } @@ -172,6 +176,10 @@ export class RequestManager { // If the tile url has /raster/v1/, make the final URL mapbox://raster/.... const rasterPrefix = `/${config.RASTER_URL_PREFIX}/`; result += `raster/${urlObject.path.replace(rasterPrefix, '')}`; + } else if (urlObject.path.match(/^\/rasterarrays\/v1\//)) { + // If the tile url has /rasterarrays/v1/, make the final URL mapbox://rasterarrays/.... + const rasterPrefix = `/${config.RASTERARRAYS_URL_PREFIX}/`; + result += `rasterarrays/${urlObject.path.replace(rasterPrefix, '')}`; } else { const tilesPrefix = `/${config.TILE_URL_VERSION}/`; result += `tiles/${urlObject.path.replace(tilesPrefix, '')}`; @@ -230,38 +238,6 @@ export class RequestManager { } } -export function isMapboxURL(url: string): boolean { - return url.indexOf('mapbox:') === 0; -} - -export function isMapboxHTTPURL(url: string): boolean { - return config.API_URL_REGEX.test(url); -} - -export function isMapboxHTTPCDNURL(url: string): boolean { - return config.API_CDN_URL_REGEX.test(url); -} - -export function isMapboxHTTPStyleURL(url: string): boolean { - return config.API_STYLE_REGEX.test(url) && !isMapboxHTTPSpriteURL(url); -} - -export function isMapboxHTTPTileJSONURL(url: string): boolean { - return config.API_TILEJSON_REGEX.test(url); -} - -export function isMapboxHTTPSpriteURL(url: string): boolean { - return config.API_SPRITE_REGEX.test(url); -} - -export function isMapboxHTTPFontsURL(url: string): boolean { - return config.API_FONTS_REGEX.test(url); -} - -export function hasCacheDefeatingSku(url: string): boolean { - return url.indexOf('sku=') > 0 && isMapboxHTTPURL(url); -} - function getAccessToken(params: Array): string | null { for (const param of params) { const match = param.match(/^access_token=(.*)$/); @@ -312,7 +288,7 @@ function parseAccessToken(accessToken: ?string) { } } -type TelemetryEventType = 'appUserTurnstile' | 'map.load' | 'map.auth' | 'gljs.performance'; +type TelemetryEventType = 'appUserTurnstile' | 'map.load' | 'map.auth' | 'gljs.performance' | 'style.load'; class TelemetryEvent { eventData: any; @@ -351,12 +327,12 @@ class TelemetryEvent { if (isLocalStorageAvailable) { //Retrieve cached data try { - const data = window.localStorage.getItem(storageKey); + const data = localStorage.getItem(storageKey); if (data) { this.eventData = JSON.parse(data); } - const uuid = window.localStorage.getItem(uuidKey); + const uuid = localStorage.getItem(uuidKey); if (uuid) this.anonId = uuid; } catch (e) { warnOnce('Unable to read from LocalStorage'); @@ -368,11 +344,12 @@ class TelemetryEvent { const isLocalStorageAvailable = storageAvailable('localStorage'); const storageKey = this.getStorageKey(); const uuidKey = this.getStorageKey('uuid'); - if (isLocalStorageAvailable) { + const anonId = this.anonId; + if (isLocalStorageAvailable && anonId) { try { - window.localStorage.setItem(uuidKey, this.anonId); + localStorage.setItem(uuidKey, anonId); if (Object.keys(this.eventData).length >= 1) { - window.localStorage.setItem(storageKey, JSON.stringify(this.eventData)); + localStorage.setItem(storageKey, JSON.stringify(this.eventData)); } } catch (e) { warnOnce('Unable to write to LocalStorage'); @@ -514,6 +491,90 @@ export class MapLoadEvent extends TelemetryEvent { }, customAccessToken); } + + remove() { + // $FlowFixMe[incompatible-type] + this.errorCb = null; + } +} + +type StyleLoadEventInput = { + map: MapboxMap; + style: string; + importedStyles: string[]; +} + +type StyleLoadEventPayload = { + mapInstanceId: string; + eventId: number; + style: string; + importedStyles?: string[]; +} + +export class StyleLoadEvent extends TelemetryEvent { + eventIdPerMapInstanceMap: Map; + mapInstanceIdMap: WeakMap; + + constructor() { + super('style.load'); + this.eventIdPerMapInstanceMap = new Map(); + this.mapInstanceIdMap = new WeakMap(); + } + + getMapInstanceId(map: MapboxMap): string { + let instanceId = this.mapInstanceIdMap.get(map); + + if (!instanceId) { + instanceId = uuid(); + this.mapInstanceIdMap.set(map, instanceId); + } + + return instanceId; + } + + getEventId(mapInstanceId: string): number { + const eventId = this.eventIdPerMapInstanceMap.get(mapInstanceId) || 0; + this.eventIdPerMapInstanceMap.set(mapInstanceId, eventId + 1); + return eventId; + } + + postStyleLoadEvent(customAccessToken: ?string, input: StyleLoadEventInput) { + const { + map, + style, + importedStyles, + } = input; + + if (!config.EVENTS_URL || !(customAccessToken || config.ACCESS_TOKEN)) { + return; + } + + const mapInstanceId = this.getMapInstanceId(map); + const payload: StyleLoadEventPayload = { + mapInstanceId, + eventId: this.getEventId(mapInstanceId), + style, + }; + + if (importedStyles.length) { + payload.importedStyles = importedStyles; + } + + this.queueRequest({ + timestamp: Date.now(), + payload + }, customAccessToken); + } + + processRequests(customAccessToken?: ?string) { + if (this.pendingRequest || this.queue.length === 0) { + return; + } + + const {timestamp, payload} = this.queue.shift(); + + this.postEvent(timestamp, payload, () => {}, customAccessToken); + } } export class MapSessionAPI extends TelemetryEvent { @@ -576,6 +637,11 @@ export class MapSessionAPI extends TelemetryEvent { } }, customAccessToken); } + + remove() { + // $FlowFixMe[incompatible-type] + this.errorCb = null; + } } export class TurnstileEvent extends TelemetryEvent { @@ -652,17 +718,21 @@ const turnstileEvent_ = new TurnstileEvent(); // $FlowFixMe[method-unbinding] export const postTurnstileEvent: (tileUrls: Array, customAccessToken?: ?string) => void = turnstileEvent_.postTurnstileEvent.bind(turnstileEvent_); -const mapLoadEvent_ = new MapLoadEvent(); +export const mapLoadEvent: MapLoadEvent = new MapLoadEvent(); +// $FlowFixMe[method-unbinding] +export const postMapLoadEvent: (number, string, ?string, EventCallback) => void = mapLoadEvent.postMapLoadEvent.bind(mapLoadEvent); + +export const styleLoadEvent: StyleLoadEvent = new StyleLoadEvent(); // $FlowFixMe[method-unbinding] -export const postMapLoadEvent: (number, string, ?string, EventCallback) => void = mapLoadEvent_.postMapLoadEvent.bind(mapLoadEvent_); +export const postStyleLoadEvent: (?string, StyleLoadEventInput) => void = styleLoadEvent.postStyleLoadEvent.bind(styleLoadEvent); export const performanceEvent_: PerformanceEvent = new PerformanceEvent(); // $FlowFixMe[method-unbinding] export const postPerformanceEvent: (?string, LivePerformanceData) => void = performanceEvent_.postPerformanceEvent.bind(performanceEvent_); -const mapSessionAPI_ = new MapSessionAPI(); +export const mapSessionAPI: MapSessionAPI = new MapSessionAPI(); // $FlowFixMe[method-unbinding] -export const getMapSessionAPI: (number, string, ?string, EventCallback) => void = mapSessionAPI_.getSessionAPI.bind(mapSessionAPI_); +export const getMapSessionAPI: (number, string, ?string, EventCallback) => void = mapSessionAPI.getSessionAPI.bind(mapSessionAPI); const authenticatedMaps = new Set(); export function storeAuthState(gl: WebGL2RenderingContext, state: boolean) { diff --git a/src/util/mapbox_url.js b/src/util/mapbox_url.js new file mode 100644 index 00000000000..b4a75f49552 --- /dev/null +++ b/src/util/mapbox_url.js @@ -0,0 +1,34 @@ +// @flow +import config from './config.js'; + +export function isMapboxHTTPURL(url: string): boolean { + return config.API_URL_REGEX.test(url); +} + +export function isMapboxURL(url: string): boolean { + return url.indexOf('mapbox:') === 0; +} + +export function isMapboxHTTPCDNURL(url: string): boolean { + return config.API_CDN_URL_REGEX.test(url); +} + +export function isMapboxHTTPSpriteURL(url: string): boolean { + return config.API_SPRITE_REGEX.test(url); +} + +export function isMapboxHTTPStyleURL(url: string): boolean { + return config.API_STYLE_REGEX.test(url) && !isMapboxHTTPSpriteURL(url); +} + +export function isMapboxHTTPTileJSONURL(url: string): boolean { + return config.API_TILEJSON_REGEX.test(url); +} + +export function isMapboxHTTPFontsURL(url: string): boolean { + return config.API_FONTS_REGEX.test(url); +} + +export function hasCacheDefeatingSku(url: string): boolean { + return url.indexOf('sku=') > 0 && isMapboxHTTPURL(url); +} diff --git a/src/util/offscreen_canvas_supported.js b/src/util/offscreen_canvas_supported.js index d487218e937..03e9de9aa91 100644 --- a/src/util/offscreen_canvas_supported.js +++ b/src/util/offscreen_canvas_supported.js @@ -1,13 +1,11 @@ -// @flow -import window from './window.js'; - +// @flow strict let supportsOffscreenCanvas: ?boolean; export default function offscreenCanvasSupported(): boolean { if (supportsOffscreenCanvas == null) { - supportsOffscreenCanvas = window.OffscreenCanvas && - new window.OffscreenCanvas(1, 1).getContext('2d') && - typeof window.createImageBitmap === 'function'; + supportsOffscreenCanvas = self.OffscreenCanvas && + new OffscreenCanvas(1, 1).getContext('2d') && + typeof self.createImageBitmap === 'function'; } return supportsOffscreenCanvas; diff --git a/src/util/performance.js b/src/util/performance.js index 413e7594962..3c077c7fc69 100644 --- a/src/util/performance.js +++ b/src/util/performance.js @@ -1,11 +1,9 @@ // @flow -import window from '../util/window.js'; import { PerformanceMarkers, LivePerformanceUtils } from '../util/live_performance.js'; -const performance = window.performance; performance.mark('library-evaluate'); @@ -64,6 +62,7 @@ export const PerformanceUtils = { placementTime += time; }, frame(timestamp: number, isRenderFrame: boolean) { + // $FlowFixMe[extra-arg] performance.mark('frame', { detail: { timestamp, @@ -102,7 +101,9 @@ export const PerformanceUtils = { getWorkerPerformanceMetrics(): { timeOrigin: string, entries: Array, scope: string } { const entries = performance.getEntries().map(entry => { const result = entry.toJSON(); + // $FlowFixMe[prop-missing] if (entry.detail) { + // $FlowFixMe[incompatible-use] Object.assign(result, { detail: entry.detail }); @@ -111,6 +112,7 @@ export const PerformanceUtils = { }); return { scope: isWorker() ? 'Worker' : 'Window', + // $FlowFixMe[prop-missing] timeOrigin: performance.timeOrigin, entries }; @@ -119,6 +121,9 @@ export const PerformanceUtils = { export function getPerformanceMeasurement(request: ?RequestParameters): Array { const url = request ? request.url.toString() : undefined; + if (!url) { + return []; + } return performance.getEntriesByName(url); } diff --git a/src/util/scheduler.js b/src/util/scheduler.js index 1b603d59431..0f7112a936c 100644 --- a/src/util/scheduler.js +++ b/src/util/scheduler.js @@ -38,7 +38,7 @@ class Scheduler { this.nextId = 0; } - add(fn: TaskFunction, metadata: TaskMetadata): Cancelable { + add(fn: TaskFunction, metadata: TaskMetadata): Cancelable | null { const id = this.nextId++; const priority = getPriority(metadata); @@ -50,9 +50,8 @@ class Scheduler { } finally { if (m) PerformanceUtils.endMeasure(m); } - return { - cancel: () => {} - }; + // Don't return an empty cancel because we can't actually be cancelled + return null; } this.tasks[id] = {fn, metadata, priority, id}; diff --git a/src/util/struct_array.js b/src/util/struct_array.js index c93dd5ff41f..d56fd8d6129 100644 --- a/src/util/struct_array.js +++ b/src/util/struct_array.js @@ -66,6 +66,12 @@ export type StructArrayLayout = { alignment: ?number } +export interface IStructArrayLayout { + _refreshViews(): void; + emplace(...args: number[]): number; + emplaceBack(...args: number[]): number; +} + export type SerializedStructArray = { length: number, arrayBuffer: ArrayBuffer @@ -92,18 +98,22 @@ export type SerializedStructArray = { * * @private */ -class StructArray { +class StructArray implements IStructArrayLayout { capacity: number; length: number; isTransferred: boolean; arrayBuffer: ArrayBuffer; + int8: Int8Array; uint8: Uint8Array; + int16: Int16Array; + uint16: Uint16Array; + int32: Int32Array; + uint32: Uint32Array; + float32: Float32Array; // The following properties are defined on the prototype. members: Array; bytesPerElement: number; - +emplaceBack: Function; - +emplace: Function; constructor() { this.isTransferred = false; @@ -193,7 +203,15 @@ class StructArray { * Create TypedArray views for the current ArrayBuffer. */ _refreshViews(): void { - throw new Error('_refreshViews() must be implemented by each concrete StructArray layout'); + throw new Error('StructArray#_refreshViews() must be implemented by each concrete StructArray layout'); + } + + emplace(..._: number[]): number { + throw new Error('StructArray#emplace() must be implemented by each concrete StructArray layout'); + } + + emplaceBack(..._: number[]): number { + throw new Error('StructArray#emplaceBack() must be implemented by each concrete StructArray layout'); } destroy() { diff --git a/src/util/struct_array_layout.js.ejs b/src/util/struct_array_layout.js.ejs index 695f0efcb91..aff2776196a 100644 --- a/src/util/struct_array_layout.js.ejs +++ b/src/util/struct_array_layout.js.ejs @@ -20,7 +20,7 @@ for (const member of members) { * * @private */ -class <%=StructArrayLayoutClass%> extends StructArray { +class <%=StructArrayLayoutClass%> extends StructArray implements IStructArrayLayout { <% for (const type of usedTypes) { -%> diff --git a/src/util/tile_request_cache.js b/src/util/tile_request_cache.js index 20e0865bed8..76ae7f4330a 100644 --- a/src/util/tile_request_cache.js +++ b/src/util/tile_request_cache.js @@ -1,7 +1,7 @@ // @flow import {warnOnce, parseCacheControl} from './util.js'; -import window from './window.js'; +import {stripQueryParameters, setQueryParameters} from './url.js'; import type Dispatcher from './dispatcher.js'; @@ -24,7 +24,7 @@ let sharedCache: ?Promise; function getCaches() { try { - return window.caches; + return caches; } catch (e) { //