diff --git a/.github/workflows/auto-build.yml b/.github/workflows/auto-build.yml index d6c0c2af0..342be27d3 100644 --- a/.github/workflows/auto-build.yml +++ b/.github/workflows/auto-build.yml @@ -1,9 +1,9 @@ name: Rath Auto Build on: push: - branches: [master, dev] + branches: [main, dev] pull_request: - branches: [master, dev] + branches: [main, dev] jobs: auto-build: runs-on: ubuntu-latest diff --git a/.github/workflows/prod-publish.yml b/.github/workflows/prod-publish.yml deleted file mode 100644 index 3dfdf952e..000000000 --- a/.github/workflows/prod-publish.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: prod publish -on: - push: - branches: [master] -jobs: - auto-build: - runs-on: ubuntu-latest - strategy: - matrix: - node-version: [16.x] - steps: - - uses: actions/checkout@v3 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node-version }} - - run: yarn install - - run: yarn workspace rath-client build - - uses: manyuanrong/setup-ossutil@master - with: - endpoint: ${{ secrets.ALIOSS_ENDPOINT }} - access-key-id: ${{ secrets.ALIOSS_ID }} - access-key-secret: ${{ secrets.ALIOSS_SECRET }} - - run: ossutil cp -rf ./packages/rath-client/build oss://${{ secrets.ALIOSS_BUCKET }} \ No newline at end of file diff --git a/package.json b/package.json index 2ccfd7275..6aacde356 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,11 @@ "version": "1.1.0", "description": "![](https://img.shields.io/github/license/Kanaries/Rath)", "scripts": { - "build": "yarn workspace rath-client build", + "build:utils": "yarn workspace @kanaries/rath-utils build", + "build:scenegraph": "yarn workspace vega-scenegraph build", + "build:renderer": "yarn workspace vega-painter-renderer build", + "build:client": "yarn workspace rath-client build", + "build": "npm run build:utils && npm run build:scenegraph && npm run build:renderer && npm run build:client", "test": "yarn workspace rath-client test", "ui": "npm run test && npm run build && node start.js", "devfront": "yarn workspace rath-client start", diff --git a/packages/rath-client/package.json b/packages/rath-client/package.json index 8d48914a3..10a202725 100644 --- a/packages/rath-client/package.json +++ b/packages/rath-client/package.json @@ -23,7 +23,7 @@ "@kanaries/loa": "^0.0.16", "@kanaries/web-data-loader": "0.1.7", "@material-ui/core": "^5.0.0-beta.5", - "@vercel/analytics": "^0.1.8", + "@vercel/analytics": "^1.0.0", "@zip.js/zip.js": "^2.6.60", "airtable": "^0.11.4", "ali-react-table": "^2.6.1", @@ -49,6 +49,7 @@ "vega": "^5.22.1", "vega-embed": "^6.21.0", "vega-lite": "^5.6.0", + "vega-painter-renderer": "*", "vega-scenegraph": "4.10.1-kanaries-patch", "visual-insights": "^0.12.3", "web-vitals": "^0.2.4", @@ -78,9 +79,12 @@ "typescript": "^4.7.4" }, "scripts": { + "build:utils": "yarn workspace @kanaries/rath-utils build", + "build:scenegraph": "yarn workspace vega-scenegraph build", + "build:renderer": "yarn workspace vega-painter-renderer build", "start": "react-app-rewired start", - "build": "GENERATE_SOURCEMAP=false react-app-rewired --max_old_space_size=8000 build", - "build2": "GENERATE_SOURCEMAP=false react-app-rewired --max_old_space_size=8000 build", + "build": "npm run build:utils && npm run build:scenegraph && npm run build:renderer && npm run build:client", + "build:client": "GENERATE_SOURCEMAP=false react-app-rewired --max_old_space_size=8000 build", "buildForAnalysis": "react-app-rewired build", "test": "jest -c ./jest.config.js --passWithNoTests --no-cache", "eject": "react-app-rewired eject", diff --git a/packages/rath-client/src/components/appNav.tsx b/packages/rath-client/src/components/appNav.tsx index 0487c8c67..fd746d8e4 100644 --- a/packages/rath-client/src/components/appNav.tsx +++ b/packages/rath-client/src/components/appNav.tsx @@ -202,7 +202,7 @@ const AppNav: React.FC = (props) => { diff --git a/packages/rath-client/src/pages/dataSource/baseActions/mainActionButton.tsx b/packages/rath-client/src/pages/dataSource/baseActions/mainActionButton.tsx index 3d6085f64..5b3efb738 100644 --- a/packages/rath-client/src/pages/dataSource/baseActions/mainActionButton.tsx +++ b/packages/rath-client/src/pages/dataSource/baseActions/mainActionButton.tsx @@ -4,6 +4,7 @@ import React, { useCallback, useMemo } from 'react'; import intl from 'react-intl-universal'; import { Menu, MenuButtonProps, MenuItem, MenuList, MenuPopover, MenuTrigger, SplitButton } from '@fluentui/react-components'; import { Poll24Regular } from '@fluentui/react-icons'; +import va from '@vercel/analytics'; import { EXPLORE_MODE, PIVOT_KEYS } from '../../../constants'; import { useGlobalStore } from '../../../store'; @@ -86,6 +87,13 @@ export const useActionModes = function () { const MainActionButton: React.FC = () => { const { satisfyAnalysisCondition, startMode, analysisOptions } = useActionModes(); + const { userStore } = useGlobalStore(); + + const startHandler = useCallback(() => { + startMode.onClick && startMode.onClick() + va.track('start_analysis', { userName: userStore.userName, mode: startMode.key }) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [startMode]); return ( @@ -93,7 +101,7 @@ const MainActionButton: React.FC = () => { {(triggerProps: MenuButtonProps) => ( startMode.onClick && startMode.onClick() }} + primaryActionButton={{ onClick: startHandler }} menuButton={triggerProps} appearance="primary" icon={} diff --git a/packages/rath-client/src/pages/painter/index.tsx b/packages/rath-client/src/pages/painter/index.tsx index 9d8248247..568f92dd1 100644 --- a/packages/rath-client/src/pages/painter/index.tsx +++ b/packages/rath-client/src/pages/painter/index.tsx @@ -16,15 +16,17 @@ import { import { toJS } from 'mobx'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import embed, { vega } from 'vega-embed'; -import { Item, ScenegraphEvent } from 'vega'; +import { Item, ScenegraphEvent, renderModule } from 'vega'; import intl from 'react-intl-universal'; +//@ts-ignore +import { PainterModule, paint, startPaint, stopPaint } from 'vega-painter-renderer'; import { IVegaSubset, PAINTER_MODE } from '../../interfaces'; import { useGlobalStore } from '../../store'; import { deepcopy, getRange } from '../../utils'; import { transVegaSubset2Schema } from '../../utils/transform'; import { viewSampling } from '../../lib/stat/sampling'; import { Card } from '../../components/card'; -import { batchMutInCatRange, batchMutInCircle, clearAggregation, debounceShouldNeverBeUsed, labelingData } from './utils'; +import { isContinuous, clearAggregation, debounceShouldNeverBeUsed, labelingData } from './utils'; import EmbedAnalysis from './embedAnalysis'; import { useViewData } from './viewDataHook'; import { COLOR_CELLS, LABEL_FIELD_KEY, LABEL_INDEX, PAINTER_MODE_LIST } from './constants'; @@ -33,6 +35,8 @@ import EmptyError from './emptyError'; import Operations from './operations'; import CanvasContainer from './canvasContainer'; +renderModule('painter', PainterModule); + const Cont = styled.div` /* cursor: none !important; */ `; @@ -157,173 +161,135 @@ const Painter: React.FC = (props) => { // @ts-ignore embed(container.current, painterSpec, { actions: painterMode === PAINTER_MODE.MOVE, + renderer: 'painter', }).then((res) => { + let changes = vega.changeset(); + let removes = new Set(); res.view.change( 'dataSource', - vega - .changeset() + changes .remove(() => true) .insert(viewData) - ); + ).runAsync().then((view) => { + // if (testConfig.printLog) { window.console.log("changes =", changes); } + }); + + setRealPainterSize((res.view as unknown as { _width: number })._width * painterSize); if (!(painterSpec.encoding.x && painterSpec.encoding.y)) return; + // if(testConfig.printLog) { + // res.view.addDataListener('dataSource', (name, value) => { + // window.console.log("dataListener", name, value); + // window.console.log(testConfig); + // }) + // } const xField = painterSpec.encoding.x.field; const yField = painterSpec.encoding.y.field; const xFieldType = painterSpec.encoding.x.type as ISemanticType; const yFieldType = painterSpec.encoding.y.type as ISemanticType; + const isContX = isContinuous(xFieldType), isContY = isContinuous(yFieldType); const limitFields: string[] = []; if (painterSpec.encoding.column) limitFields.push(painterSpec.encoding.column.field); if (painterSpec.encoding.row) limitFields.push(painterSpec.encoding.row.field); - if (xFieldType === 'quantitative' && yFieldType === 'quantitative') { - const xRange = getRange(viewData.map((r) => r[xField])); - const yRange = getRange(viewData.map((r) => r[yField])); - const hdr = (e: ScenegraphEvent, item: Item | null | undefined) => { + + const [rotXField, rotYField] = !isContX ? [yField, xField] : [xField, yField]; + const [rotIsContX, rotIsContY] = !isContX ? [isContY, isContX] : [isContX, isContY]; + let hdr = (e: ScenegraphEvent, item: Item | null | undefined) => { + // window.console.warn('hdr case', [xFieldType, yField], 'not implemented'); + }; + if (rotIsContX && rotIsContY) { + const xRange = getRange(viewData.map((r) => r[rotXField])); + const yRange = getRange(viewData.map((r) => r[rotYField])); + hdr = (e: ScenegraphEvent, item: Item | null | undefined) => { e.stopPropagation(); e.preventDefault(); // @ts-ignore if (!isPainting.current && e.vegaType !== 'touchmove') return; + startPaint(res.view); if (painting && item && item.datum) { - const { mutIndices, mutValues } = batchMutInCircle({ - mutData: viewData, - fields: [xField, yField], - point: [item.datum[xField], item.datum[yField]], - a: xRange[1] - xRange[0], - b: yRange[1] - yRange[0], - r: painterSize / 2, - key: LABEL_FIELD_KEY, - indexKey: LABEL_INDEX, - value: mutFeatValues[mutFeatIndex], - datum: item.datum, - painterMode, - limitFields - }); - if (painterMode === PAINTER_MODE.COLOR) { - linkNearViz(); - res.view - .change( - 'dataSource', - vega - .changeset() - .remove((r: any) => mutIndices.has(r[LABEL_INDEX])) - .insert(mutValues) - ) - .runAsync(); - } else if (painterMode === PAINTER_MODE.ERASE) { - res.view - .change( - 'dataSource', - vega.changeset().remove((r: any) => mutIndices.has(r[LABEL_INDEX])) - ) - .runAsync(); - maintainViewDataRemove((r: any) => mutIndices.has(r[LABEL_INDEX])); + let limits: { [key: string]: any } = {}; + for (let f of limitFields) { + limits[f] = item.datum[f]; } - } - }; - res.view.addEventListener('mousedown', () => { - isPainting.current = true; - }); - res.view.addEventListener('mouseup', () => { - isPainting.current = false; - }); - res.view.addEventListener('mousemove', hdr); - res.view.addEventListener('touchmove', hdr); - } else if (xFieldType !== 'quantitative' && yFieldType === 'quantitative') { - const yRange = getRange(viewData.map((r) => r[yField])); - const hdr = (e: ScenegraphEvent, item: Item | null | undefined) => { - e.stopPropagation(); - e.preventDefault(); - // @ts-ignore - if (!isPainting.current && e.vegaType !== 'touchmove') return; - if (painting && item && item.datum) { - const { mutIndices, mutValues } = batchMutInCatRange({ - mutData: viewData, - fields: [xField, yField], - point: [item.datum[xField], item.datum[yField]], - r: painterSize / 2, - key: LABEL_FIELD_KEY, - range: yRange[1] - yRange[0], + /** directly setting 'fill' of scenegraph */ + const result = paint({ + view: res.view, + painterMode, + fields: [rotXField, rotYField], + point: [item.datum[rotXField], item.datum[rotYField]], + radius: painterSize / 2, + range: [xRange[1] - xRange[0], yRange[1] - yRange[0]], + limits: limits, + groupValue: mutFeatValues[mutFeatIndex], indexKey: LABEL_INDEX, - value: mutFeatValues[mutFeatIndex], + newColor: COLOR_CELLS[mutFeatIndex].color, }); + const { mutIndices, mutValues, view } = result; + res.view = view; if (painterMode === PAINTER_MODE.COLOR) { - linkNearViz(); - res.view - .change( - 'dataSource', - vega - .changeset() - .remove((r: any) => mutIndices.has(r[LABEL_INDEX])) - .insert(mutValues) - ) - .runAsync(); + changes = changes + .remove((r: any) => mutIndices.has(r[LABEL_INDEX])) + .insert(mutValues) } else if (painterMode === PAINTER_MODE.ERASE) { - res.view - .change( - 'dataSource', - vega.changeset().remove((r: any) => mutIndices.has(r[LABEL_INDEX])) - ) - .runAsync(); - maintainViewDataRemove((r: any) => mutIndices.has(r[LABEL_INDEX])); + changes = changes + .remove((r: any) => mutIndices.has(r[LABEL_INDEX])); + for (let i of mutIndices) removes.add(i); } } }; - res.view.addEventListener('mousedown', () => { - isPainting.current = true; - }); - res.view.addEventListener('mouseup', () => { - isPainting.current = false; - }); - res.view.addEventListener('mousemove', hdr); - res.view.addEventListener('touchmove', hdr); - } else if (yFieldType !== 'quantitative' && xFieldType === 'quantitative') { - const hdr = (e: ScenegraphEvent, item: Item | null | undefined) => { + } else if (rotIsContX && !rotIsContY) { + const xRange = getRange(viewData.map((r) => r[rotXField])); + hdr = (e: ScenegraphEvent, item: Item | null | undefined) => { e.stopPropagation(); e.preventDefault(); // @ts-ignore if (!isPainting.current && e.vegaType !== 'touchmove') return; + startPaint(res.view); if (painting && item && item.datum) { - const xRange = getRange(viewData.map((r) => r[xField])); - const { mutIndices, mutValues } = batchMutInCatRange({ - mutData: viewData, - fields: [yField, xField], - point: [item.datum[yField], item.datum[xField]], - r: painterSize / 2, + const { mutIndices, mutValues, view } = paint({ + view: res.view, + painterMode, + fields: [rotXField, rotYField], + point: [item.datum[rotXField], item.datum[rotYField]], + radius: painterSize / 2, range: xRange[1] - xRange[0], - key: LABEL_FIELD_KEY, + groupValue: mutFeatValues[mutFeatIndex], indexKey: LABEL_INDEX, - value: mutFeatValues[mutFeatIndex], + newColor: COLOR_CELLS[mutFeatIndex].color, }); + res.view = view; if (painterMode === PAINTER_MODE.COLOR) { - linkNearViz(); - res.view - .change( - 'dataSource', - vega - .changeset() - .remove((r: any) => mutIndices.has(r[LABEL_INDEX])) - .insert(mutValues) - ) - .runAsync(); + changes = changes + .remove((r: any) => mutIndices.has(r[LABEL_INDEX])) + .insert(mutValues) } else if (painterMode === PAINTER_MODE.ERASE) { - res.view - .change( - 'dataSource', - vega.changeset().remove((r: any) => mutIndices.has(r[LABEL_INDEX])) - ) - .runAsync(); - maintainViewDataRemove((r: any) => mutIndices.has(r[LABEL_INDEX])); + changes = changes.remove((r: any) => mutIndices.has(r[LABEL_INDEX])); + for (let i of mutIndices) removes.add(i); } } }; - res.view.addEventListener('mousedown', () => { - isPainting.current = true; - }); - res.view.addEventListener('mouseup', () => { - isPainting.current = false; - }); - res.view.addEventListener('mousemove', hdr); - res.view.addEventListener('touchmove', hdr); } + // else { /** !rotIsContX && !rotIsContY */ } + res.view.addEventListener('mousedown', (e) => { + isPainting.current = true; + startPaint(res.view); + }); + res.view.addEventListener('mouseup', () => { + isPainting.current = false; + stopPaint(res.view); + const curRemoves = removes, curChanges = changes; + removes = new Set(); + changes = vega.changeset(); + res.view.change('dataSource', curChanges).runAsync().then((view) => { + linkNearViz(); + maintainViewDataRemove((r: any) => curRemoves.has(r[LABEL_INDEX])); + }) + }); + // TODO: use renderer to check nearest points + // res.view.addEventListener('gl_mousemove', hdr); + // res.view.addEventListener('gl_touchmove', hdr); + res.view.addEventListener('mousemove', hdr); + res.view.addEventListener('touchmove', hdr); res.view.resize(); res.view.runAsync(); }); diff --git a/packages/rath-client/src/pages/painter/utils.ts b/packages/rath-client/src/pages/painter/utils.ts index 1cc0809f3..92e5928e8 100644 --- a/packages/rath-client/src/pages/painter/utils.ts +++ b/packages/rath-client/src/pages/painter/utils.ts @@ -1,7 +1,12 @@ import produce from 'immer'; +import { type ISemanticType } from '@kanaries/loa'; import { IRow, IVegaSubset, PAINTER_MODE } from '../../interfaces'; import { LABEL_FIELD_KEY, LABEL_INDEX } from './constants'; +export function isContinuous (fieldType: ISemanticType) { + return fieldType === 'quantitative' || fieldType === 'temporal'; +} + export function batchMutInRange (mutData: IRow, field: string, range: [number, number], key: string, value: any) { for (let i = 0; i < mutData.length; i++) { if (mutData[i][field] >= range[0] && mutData[i][field] <= range[1]) { @@ -24,6 +29,7 @@ interface BatchMutInCircleProps { limitFields: string[]; painterMode?: PAINTER_MODE } +/** @deprecated to {import('vega-painter-renderer').paint} */ export function batchMutInCircle (props: BatchMutInCircleProps) { const { mutData, @@ -83,6 +89,7 @@ interface BatchMutInCatRangeProps { value: any; indexKey: string; } +/** @deprecated to {import('vega-painter-renderer').paint} */ export function batchMutInCatRange (props: BatchMutInCatRangeProps) { const { mutData, diff --git a/packages/rath-client/src/store/userStore.ts b/packages/rath-client/src/store/userStore.ts index d02cccc60..b8596c482 100644 --- a/packages/rath-client/src/store/userStore.ts +++ b/packages/rath-client/src/store/userStore.ts @@ -1,5 +1,6 @@ import { makeAutoObservable, observable, runInAction } from 'mobx'; import { TextWriter, ZipReader } from "@zip.js/zip.js"; +import va from '@vercel/analytics'; import { DataSourceType, IAccessPageKeys, ICreateDashboardConfig, ICreateDatasetPayload, ICreateDatasetResult, ICreateDataSourcePayload, ICreateDataSourceResult, IDashboardDocumentInfo, IDatasetData, IDatasetMeta, IDataSourceMeta } from '../interfaces'; import { getMainServiceAddress } from '../utils/user'; import { notify } from '../components/error'; @@ -202,7 +203,12 @@ export default class UserStore { public async updateAuthStatus() { try { const url = getMainServiceAddress('/api/loginStatus'); - const res = await request.get<{}, { loginStatus: boolean; userName: string }>(url); + const res = await request.get<{}, { loginStatus: boolean; userName: string; userId: string }>(url); + va.track('login_status', { + loginStatus: res.loginStatus, + userName: res.userName, + userId: res.userId, + }) return res.loginStatus; } catch (error) { notify({ diff --git a/packages/utils/package.json b/packages/utils/package.json index baaa21d2d..32af966ae 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -2,8 +2,10 @@ "name": "@kanaries/rath-utils", "version": "1.0.0", "description": "", - "main": "index.js", + "main": "dist/index.js", + "types": "dist/index.d.ts", "scripts": { + "build": "tsc", "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], @@ -11,8 +13,5 @@ "license": "ISC", "devDependencies": { "typescript": "^4.7.4" - }, - "peerDependencies": { - "localforage": "^1.10.0" } } diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index e69de29bb..ae1d4a828 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -0,0 +1 @@ +export {testConfig} from './testConfig' \ No newline at end of file diff --git a/packages/utils/src/testConfig.ts b/packages/utils/src/testConfig.ts new file mode 100644 index 000000000..1daa5b1a8 --- /dev/null +++ b/packages/utils/src/testConfig.ts @@ -0,0 +1,36 @@ +function getURIConfig(name: string, _default: any) { + return decodeURIComponent( + new URL(window.location.href).searchParams + .get(name) ?? _default + ) +} +interface TestConfig { + useIndexUpdate: boolean, + useRenderer: 'painter' | 'webgl' | 'canvas' | 'svg' | 'png', + useGlobalChangeset: boolean, + printLog: boolean +} +const testConfig: TestConfig = { + useIndexUpdate: true, + useRenderer: 'painter', // 'painter' | 'webgl' | 'canvas' | 'svg' | 'png' + useGlobalChangeset: true, + printLog: false, +} +let config: {[k: string]: any} = {}; +for (let key in testConfig) { + const value = testConfig[key as keyof TestConfig]; + let v; + if (typeof value === 'string') { + v = getURIConfig(key, value); + } + else if (typeof value === 'boolean') { + v = getURIConfig(key, value ? 'true' : 'false') === 'true'; + } + else if (typeof value === 'number') { + v = Number(getURIConfig(key, value)); + } + config[key] = v; +} +Object.assign(testConfig, config); + +export {testConfig, type TestConfig}; \ No newline at end of file diff --git a/packages/utils/tsconfig.json b/packages/utils/tsconfig.json index 8f0405596..11561b902 100644 --- a/packages/utils/tsconfig.json +++ b/packages/utils/tsconfig.json @@ -5,16 +5,16 @@ /* Basic Options */ // "incremental": true, /* Enable incremental compilation */ "target": "ESNext", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */ - "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ + "module": "es2015", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ // "lib": [], /* Specify library files to be included in the compilation. */ // "allowJs": true, /* Allow javascript files to be compiled. */ // "checkJs": true, /* Report errors in .js files. */ // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */ - // "declaration": true, /* Generates corresponding '.d.ts' file. */ + "declaration": true, /* Generates corresponding '.d.ts' file. */ // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ // "sourceMap": true, /* Generates corresponding '.map' file. */ // "outFile": "./", /* Concatenate and emit output to single file. */ - // "outDir": "./", /* Redirect output structure to the directory. */ + "outDir": "./dist", /* Redirect output structure to the directory. */ // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ // "composite": true, /* Enable project compilation */ // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ @@ -68,5 +68,8 @@ /* Advanced Options */ "skipLibCheck": true, /* Skip type checking of declaration files. */ "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ - } + }, + "include": [ + "src/**/*.ts" + ], } diff --git a/packages/vega-renderer/.tsconfig.json b/packages/vega-renderer/.tsconfig.json new file mode 100644 index 000000000..99075b0fc --- /dev/null +++ b/packages/vega-renderer/.tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "rootDir": "./src", + "outDir": "dist", + "sourceMap": true, + "target": "ES2015", + "module": "ESNext", + "moduleResolution": "Node", + "strict": false, + "resolveJsonModule": true, + "esModuleInterop": true, + "jsx": "preserve", + "lib": ["ESNext", "DOM"], + "baseUrl": "./", + "emitDeclarationOnly": true, + "declaration": true, + "declarationDir": "./dist", + "allowJs": true + }, + "include": ["src/**/*", "index.js"], + "exclude": ["node_modules", "dist"] +} diff --git a/packages/vega-renderer/README.md b/packages/vega-renderer/README.md new file mode 100644 index 000000000..ad32680c0 --- /dev/null +++ b/packages/vega-renderer/README.md @@ -0,0 +1,3 @@ +# vega-painter-renderer + + diff --git a/packages/vega-renderer/babel.config.js b/packages/vega-renderer/babel.config.js new file mode 100644 index 000000000..f7c66a75c --- /dev/null +++ b/packages/vega-renderer/babel.config.js @@ -0,0 +1,14 @@ +module.exports = { + presets: [["@babel/preset-env"]], + plugins: [ + [ + "@babel/plugin-transform-runtime", + { + corejs: { + version: 3, + proposals: false, + }, + }, + ], + ], +}; diff --git a/packages/vega-renderer/example/index.html b/packages/vega-renderer/example/index.html new file mode 100644 index 000000000..d931f033d --- /dev/null +++ b/packages/vega-renderer/example/index.html @@ -0,0 +1,12 @@ + + + + + + + Document + + + + + diff --git a/packages/vega-renderer/example/index.js b/packages/vega-renderer/example/index.js new file mode 100644 index 000000000..e69de29bb diff --git a/packages/vega-renderer/index.js b/packages/vega-renderer/index.js new file mode 100644 index 000000000..c902ec49f --- /dev/null +++ b/packages/vega-renderer/index.js @@ -0,0 +1,8 @@ +export * from './src/painter' +export * from './src/webgl' +// import {PainterRenderer, PainterHandler, PainterModule} from './src/painter'; +// import {WebGLRenderer, WebGLHandler, WebGLModule} from './src/webgl'; +// export { +// PainterRenderer, PainterHandler, PainterModule, +// WebGLRenderer, WebGLHandler, WebGLModule +// } \ No newline at end of file diff --git a/packages/vega-renderer/package.json b/packages/vega-renderer/package.json new file mode 100644 index 000000000..a57d448ee --- /dev/null +++ b/packages/vega-renderer/package.json @@ -0,0 +1,40 @@ +{ + "name": "vega-painter-renderer", + "version": "1.0.0", + "description": "", + "main": "dist/vega-painter-renderer.js", + "module": "dist/vega-painter-renderer.module.js", + "types": "dist/index.d.ts", + "scripts": { + "dev": "rollup -cw", + "build": "rm -rf dist && rollup -c" + }, + "license": "", + "devDependencies": { + "@babel/cli": "^7.19.3", + "@babel/core": "^7.19.3", + "@babel/plugin-transform-runtime": "^7.19.1", + "@babel/preset-env": "^7.19.3", + "@rollup/plugin-babel": "^5.3.1", + "@rollup/plugin-commonjs": "^22.0.2", + "@rollup/plugin-node-resolve": "^14.1.0", + "@rollup/plugin-typescript": "^11.1.0", + "rollup": "^2.79.1", + "ts-lib": "^0.0.5", + "typescript": "^5.0.4", + "vega-typings": "^0.24.0" + }, + "dependencies": { + "@babel/runtime-corejs3": "^7.19.4", + "@kanaries/rath-utils": "1.0.0", + "d3-color": "*", + "d3-shape": "*", + "extrude-polyline": "^1.0.6", + "parse-svg-path": "^0.1.2", + "simplify-path": "^1.1.0", + "svg-path-contours": "^2.0.0", + "triangulate-contours": "^1.0.2", + "vega-scenegraph": "4.10.1-kanaries-patch" + }, + "browserslist": "> 0.25%, not dead" +} diff --git a/packages/vega-renderer/rollup.config.js b/packages/vega-renderer/rollup.config.js new file mode 100644 index 000000000..0dbb1e6cb --- /dev/null +++ b/packages/vega-renderer/rollup.config.js @@ -0,0 +1,45 @@ +import commonjs from "@rollup/plugin-commonjs"; +import nodeResolve from "@rollup/plugin-node-resolve"; +import babel from "@rollup/plugin-babel"; +// import typescript from "@rollup/plugin-typescript"; +import { defineConfig } from "rollup"; +import bundleSize from 'rollup-plugin-bundle-size'; +import { terser } from 'rollup-plugin-terser'; +import pkg from "./package.json"; + +export default defineConfig({ + strictDeprecations: true, + input: "index.js", + output: [ + { + file: pkg.module, + format: "esm", + sourcemap: false, + }, + { + file: pkg.main, + format: "umd", + sourcemap: false, + name: "vega-painter-renderer", + }, + ], + plugins: [ + nodeResolve({ + browser: true, + modulesOnly: true, + customResolveOptions: { preserveSymlinks: false } + }), + commonjs(), + babel({ + exclude: "node_modules/**", + extensions: [".js", ".ts"], + babelHelpers: "runtime", + }), + // typescript({ + // sourceMap: true, + // tsconfig: "./tsconfig.json", + // declaration: true, + // declarationDir: "./dist", + // }), + ], +}); diff --git a/packages/vega-renderer/src/painter/PainterHandler.js b/packages/vega-renderer/src/painter/PainterHandler.js new file mode 100644 index 000000000..a85354f18 --- /dev/null +++ b/packages/vega-renderer/src/painter/PainterHandler.js @@ -0,0 +1,161 @@ +import {WebGLHandler} from '../webgl'; +import {point, domFind, Marks} from 'vega-scenegraph'; + +import { + ClickEvent, DragEnterEvent, DragLeaveEvent, DragOverEvent, Events, + HrefEvent, MouseDownEvent, MouseMoveEvent, MouseOutEvent, MouseOverEvent, + MouseWheelEvent, TooltipHideEvent, TooltipShowEvent, + TouchEndEvent, TouchMoveEvent, TouchStartEvent +} from './util/events'; +import { + eventBundle, eventListenerCheck, move, inactive, showEvent, union, pipe, gl_evt +} from './util/handler' +import { isArray } from "vega-util"; +import { testConfig } from "@kanaries/rath-utils"; + +Events.push( + ... [ClickEvent, MouseDownEvent, MouseMoveEvent, MouseOutEvent, DragLeaveEvent] + .map(ev => `gl_${ev}`) +) + +/** + * @class PainterHandler + * @param {Loader} customLoader + * @param {Tooltip} customTooltip + * @constructor PainterHandler + * + * @event PainterHandler#click + * @event PainterHandler#move + */ +class PainterHandler extends WebGLHandler { + constructor(customLoader, customTooltip) { + if (testConfig.printLog) { + console.log('PainterHandler())', customLoader, customTooltip); + } + super(customLoader, customTooltip); + } + /** + * + * @param {HTMLElement} el + * @param {[number, number]} origin + * @param {View} obj + * @returns + */ + initialize(el, origin, obj) { + if (testConfig.printLog) { + console.log('PainterHandler.initialize()', el, origin, obj); + } + // build here + const init = super.initialize(el, origin, obj); + return init; + } + context() { + return super.context(); + } + + pickEvent(evt) { + const p = point(evt, this._canvas), + o = this._origin; + return this.pick(this._scene, p[0], p[1], p[0] - o[0], p[1] - o[1]); + } + + // find the scenegraph item at the current mouse position + // x, y -- the absolute x, y mouse coordinates on the canvas element + // gx, gy -- the relative coordinates within the current group + pick(scene, x, y, gx, gy) { + const g = this.context(), + mark = Marks[scene.marktype]; + return mark.pick.call(this, g, scene, x, y, gx, gy); + } + + // on(type, handler) { + // } + // off(type, handler) { + // } + + // to keep old versions of firefox happy + DOMMouseScroll(evt) { + this.fire(MouseWheelEvent, evt); + } + + mousemove = union(gl_evt(MouseMoveEvent), move(MouseMoveEvent, MouseOverEvent, MouseOutEvent)); + dragover = union(gl_evt(DragOverEvent), move(DragOverEvent, DragEnterEvent, DragLeaveEvent)); + mouseout = union(gl_evt(MouseOutEvent), inactive(MouseOutEvent)); + dragleave = union(gl_evt(DragLeaveEvent), inactive(DragLeaveEvent)); + + mousedown = union(gl_evt(MouseDownEvent), function(evt) { + this._down = this._active; + // console.log("Canvas mousedown", this); + this.fire(MouseDownEvent, evt); + }); + + click = union(gl_evt(ClickEvent), (evt) => { + + if (this._down === this._active) { + this.fire(ClickEvent, evt); + this._down = null; + } + }); + + touchstart = union(gl_evt(TouchStartEvent), super.touchstart) + + touchmove = union(gl_evt(TouchMoveEvent), super.touchmove); + + touchend = union(gl_evt(TouchEndEvent), super.touchend); + + // fire an event + fire(type, evt, touch) { + const a = touch ? this._touch : this._active, + h = this._handlers[type]; + + // set event type relative to scenegraph items + evt.vegaType = type; + + // handle hyperlinks and tooltips first + if (type === HrefEvent && a && a.href) { + this.handleHref(evt, a, a.href); + } else if (type === TooltipShowEvent || type === TooltipHideEvent) { + if (this._obj._renderer.isPainting) type = TooltipHideEvent; // hide it when painting + this.handleTooltip(evt, a, type !== TooltipHideEvent); + } + + // invoke all registered handlers + if (h) { + for (let i=0, len=h.length; i= 0) { + h.splice(i, 1); + } + + return this; + } +} + +export default PainterHandler; \ No newline at end of file diff --git a/packages/vega-renderer/src/painter/PainterRenderer.js b/packages/vega-renderer/src/painter/PainterRenderer.js new file mode 100644 index 000000000..4f164d2fa --- /dev/null +++ b/packages/vega-renderer/src/painter/PainterRenderer.js @@ -0,0 +1,169 @@ +/// +import {sceneVisit, Bounds} from 'vega-scenegraph'; +import {WebGLRenderer} from '../webgl'; +import {drawImage, loadImageAndCreateTextureInfo} from '../webgl/util/image'; +import { testConfig } from '@kanaries/rath-utils/dist/testConfig'; + +class PainterRenderer extends WebGLRenderer { + constructor(imageLoader) { + super(imageLoader); + } + initialize(el, width, height, origin) { + if (testConfig.printLog) { + console.trace("Painter Renderer"); + } + const init = super.initialize(el, width, height, origin); + return init; + } + set isPainting(painting) { + this._painting = painting; + } + get isPainting() { + return !!this._painting; + } + resize(width, height, origin) { + const resize = super.resize(width, height, origin); + return resize; + } + _render(scene, items) { + + var gl = this.context(), + b, i; + + gl._tx = 0; + gl._ty = 0; + gl._triangleGeometry = []; + gl._triangleColor = []; + if (gl._images) { + for (i = 0; i < gl._images.length; i++) { + gl.deleteTexture(gl._images[i].texture); + } + } + gl._images = []; + gl._randomZ = this._randomZ; + gl._pathCacheHit = 0; + gl._pathCacheMiss = 0; + gl._itemCacheHit = 0; + gl._itemCacheMiss = 0; + gl._shapeCacheHit = 0; + gl._shapeCacheMiss = 0; + + b = (!items || this._redraw) + ? (this._redraw = false, null) + // : clipToBounds(gl, items); + : undefined; + + if (items) { + for (i = 0; i < items.length; i++) { + items[i]._dirty = true; + if (items[i].exit && sceneMarks[items[i].mark.marktype].nested && items[i].mark.items.length) { + // Mark an item as dirty to force redraw of the nested mark + items[i].mark.items[0]._dirty = true; + } + } + } else { + gl._fullRedraw = true; + } + + if (this._depthTest) { + gl.enable(gl.DEPTH_TEST); + } else { + gl.disable(gl.DEPTH_TEST); + } + + this._updateUniforms(); + + this.clear(); + + this.draw(gl, scene, b); + + var imgInfo = loadImageAndCreateTextureInfo(gl, gl._textCanvas); + imgInfo.x = 0; + imgInfo.y = 0; + imgInfo.w = gl.canvas.width / gl._ratio; + imgInfo.h = gl.canvas.height / gl._ratio; + gl._images.push(imgInfo); + + for (i = 0; i < gl._images.length; i++) { + drawImage(gl, gl._images[i], this.matrix); + } + + if (items) { + for (i = 0; i < items.length; i++) { + items[i]._dirty = false; + } + } + gl._fullRedraw = false; + this._lastScene = scene; + + // console.log('Path cache hit: ' + gl._pathCacheHit); + // console.log('Path cache miss: ' + gl._pathCacheMiss); + // console.log('Item cache hit: ' + gl._itemCacheHit); + // console.log('Item cache miss: ' + gl._itemCacheMiss); + // console.log('Shape cache hit: ' + gl._shapeCacheHit); + // console.log('Shape cache miss: ' + gl._shapeCacheMiss); + + return this; + } + clear() { + super.clear(); + } + /** + * + * @param {import('vega-typings/types').Scene} scene + * @param {(item: import('vega-typings/types').Item) => any} visitor visitor + * @param {Bounds} bounds + */ + visitBound(scene, visitor, bounds) { + // TODO: consider Bounds + function dfs(item) { + if (item.datum) { + visitor(item); + } + sceneVisit(item, dfs); + } + sceneVisit(scene.root, dfs); + } + /** + * Get lowest level interactive scene items. + * @param {import('vega-typings').Scene} scene + * @returns {import('vega-typings').Item[]} + */ + selectInteractive(scene) { + if (scene === this._lastScene && this._painter_interactive) return this._painter_interactive; + const selected = []; + let index = 0; + function visit(item) { + if (item.interactive === false) return; + item._ig_start = index; + if (item.items) for (let i of item.items) { + visit(i); + } + item._ig_end = index; + if (item.interactive === true && item._ig_start === item._ig_end) { + selected.push(item); + ++index; + ++item._ig_end; + } + } + visit(scene.root); + return this._painter_interactive = selected + } + /** + * + * @param {import('vega-typings/types').Scene} scene + * @param {Bounds} bounds + * @returns {import('vega-typings/types').Item[]} + */ + selectItems(scene, bounds = new Bounds()) { + let interactive = this.selectInteractive(scene); + if (scene === this._lastScene && this._lastBounds && bounds.equals(this._lastBounds)) return this._lastSelected; + this._lastBounds = bounds; + const selected = []; + for (let item of interactive) { + if (bounds.contains(item.bounds)) sceneVisit(item, item => { if (item.datum) selected.push(item) }); + } + return this._lastSelected = selected; + } +} +export default PainterRenderer; \ No newline at end of file diff --git a/packages/vega-renderer/src/painter/index.js b/packages/vega-renderer/src/painter/index.js new file mode 100644 index 000000000..1884862c0 --- /dev/null +++ b/packages/vega-renderer/src/painter/index.js @@ -0,0 +1,11 @@ +import PainterHandler from "./PainterHandler"; +import PainterRenderer from "./PainterRenderer"; +export * from "./util/paint"; + +export { PainterHandler, PainterRenderer }; + +export const PainterModule = { + renderer: PainterRenderer, + headless: PainterRenderer, + handler: PainterHandler, +} \ No newline at end of file diff --git a/packages/vega-renderer/src/painter/interface.d.ts b/packages/vega-renderer/src/painter/interface.d.ts new file mode 100644 index 000000000..de79dee9c --- /dev/null +++ b/packages/vega-renderer/src/painter/interface.d.ts @@ -0,0 +1,22 @@ +import type { View } from 'vega-typings'; +interface IPainterDrawConfig { + view: View; + painterMode: 'color' | 'clean'; + fields: [string, string]; + point: [number, number]; + radius: number; + /** + * [number,number]: data extent of both fields, or \ + * number: data extent of fields[0] */ + range: [number, number] | number; + /** Select item iff `item.datum[key] === limites[key]`, for all keys of `limits` */ + limits: { [key: string]: any }; + /** The key to store the `groupValue`. `_lab_field` by default */ + groupKey?: string; + /** `groupValue` equals to "_selected" by default */ + groupValue?: any; + /** The key of mutIndices to return */ + indexKey?: any; + /** `"#ffffff for ERASE and #000000 for COLOR by default" */ + newColor?: string; // hex color +} diff --git a/packages/vega-renderer/src/painter/util/events.js b/packages/vega-renderer/src/painter/util/events.js new file mode 100644 index 000000000..81a45b65f --- /dev/null +++ b/packages/vega-renderer/src/painter/util/events.js @@ -0,0 +1,45 @@ +export const KeyDownEvent = 'keydown'; +export const KeyPressEvent = 'keypress'; +export const KeyUpEvent = 'keyup'; +export const DragEnterEvent = 'dragenter'; +export const DragLeaveEvent = 'dragleave'; +export const DragOverEvent = 'dragover'; +export const MouseDownEvent = 'mousedown'; +export const MouseUpEvent = 'mouseup'; +export const MouseMoveEvent = 'mousemove'; +export const MouseOutEvent = 'mouseout'; +export const MouseOverEvent = 'mouseover'; +export const ClickEvent = 'click'; +export const DoubleClickEvent = 'dblclick'; +export const WheelEvent = 'wheel'; +export const MouseWheelEvent = 'mousewheel'; +export const TouchStartEvent = 'touchstart'; +export const TouchMoveEvent = 'touchmove'; +export const TouchEndEvent = 'touchend'; + +export const Events = [ + KeyDownEvent, + KeyPressEvent, + KeyUpEvent, + DragEnterEvent, + DragLeaveEvent, + DragOverEvent, + MouseDownEvent, + MouseUpEvent, + MouseMoveEvent, + MouseOutEvent, + MouseOverEvent, + ClickEvent, + DoubleClickEvent, + WheelEvent, + MouseWheelEvent, + TouchStartEvent, + TouchMoveEvent, + TouchEndEvent +]; + +export const TooltipShowEvent = MouseMoveEvent; + +export const TooltipHideEvent = MouseOutEvent; + +export const HrefEvent = ClickEvent; diff --git a/packages/vega-renderer/src/painter/util/handler.js b/packages/vega-renderer/src/painter/util/handler.js new file mode 100644 index 000000000..7382df30a --- /dev/null +++ b/packages/vega-renderer/src/painter/util/handler.js @@ -0,0 +1,126 @@ +import { point } from 'vega-scenegraph'; +import { TouchEndEvent, TouchMoveEvent, TouchStartEvent } from './events'; +import { isArray } from "vega-util"; +import { testConfig } from '@kanaries/rath-utils'; + +const eventBundle = (type) => (( + type === TouchStartEvent || + type === TouchMoveEvent || + type === TouchEndEvent +) +? [TouchStartEvent, TouchMoveEvent, TouchEndEvent] +: [type]); + +// lazily add listeners to the canvas as needed +function eventListenerCheck(handler, type) { + eventBundle(type).forEach(_ => addEventListener(handler, _)); +} + +function addEventListener(handler, type) { + const canvas = handler.canvas(); + if (canvas && !handler._events[type]) { + handler._events[type] = 1; + canvas.addEventListener(type, handler[type] + ? evt => handler[type](evt) + : evt => handler.fire(type, evt) + ); + } +} + +function move(moveEvent, overEvent, outEvent) { + return function(evt) { + const a = this._active, + p = this.pickEvent(evt); + + if (p === a) { + // active item and picked item are the same + this.fire(moveEvent, evt); // fire move + } else { + // active item and picked item are different + if (!a || !a.exit) { + // fire out for prior active item + // suppress if active item was removed from scene + this.fire(outEvent, evt); + } + // console.log("fired event", p, evt); + this._active = p; // set new active item + this.fire(overEvent, evt); // fire over for new active item + this.fire(moveEvent, evt); // fire move for new active item + } + }; +} + +function inactive(type) { + return function(evt) { + this.fire(type, evt); + this._active = null; + }; +} + +/** + * show event + * depends on `this`, should only be used as object methods + * @param {(evt: Event) => any} handler + * @returns + */ +function showEvent(handler) { + return function(evt) { + if (testConfig.printLog) { + console.log("ev:", evt); + } + return handler.call(this, evt); + } +} + +/** + * merge several event handlers + * @param {...(evt: Event)=>any} handlers + * @returns + */ +function union(...handlers) { + return function(evt) { + for (let i = 0; i < handlers.length; ++i) { + handlers[i].call(this, evt); + } + }; +} + +/** + * + * depends on `this`, should only be used as object methods + * @param {...(handler: function(Event): any) => (function(Event): any)} middlewares + * @returns (evt: Event)=>any + */ +function pipe(...middlewares) { + return function(handler) { + let next = handler; + for (let i = middlewares.length - 1; i >= 0; --i) { + next = m(next); + } + return next; + } +} + +/** + * depends on `this`, should only be used as object methods + * @param {*} type + * @returns + */ +function gl_evt(type) { + return function(evt) { + type = type || evt.type; + const h = this._handlers[`gl_${type}`]; + const p = point(evt, this._canvas); + const o = this._origin; + [evt._x, evt._y] = p; + [evt._ox, evt._oy] = o; + evt._renderer = this._obj._renderer + if (h && isArray(h)) { + for (let i = 0; i < h.length; ++i) { + h[i].handler.call(this._obj, evt, this._obj._renderer, this._active, this._touch); + } + } + } +} + +export {eventBundle, eventListenerCheck, move, inactive, showEvent, union, pipe, gl_evt} \ No newline at end of file diff --git a/packages/vega-renderer/src/painter/util/paint.js b/packages/vega-renderer/src/painter/util/paint.js new file mode 100644 index 000000000..5b365f9d4 --- /dev/null +++ b/packages/vega-renderer/src/painter/util/paint.js @@ -0,0 +1,94 @@ +import { renderModule } from 'vega-scenegraph'; +import { PainterModule } from '..'; + +/** + * It automatically changes the view renderer to PainterModule. + * @param {import('../interface').IPainterDrawConfig} config + * @returns { {mutIndices: Set, mutValues: Datum[], changes: import('vega-typings').Changeset} } + */ +export function paint(config) { + let view = config.view; + if (view.renderer() !== 'painter') { + // TODO: [Discuss] Make `PainterModule` a plugin of the current renderer? + renderModule('painter', PainterModule); + view = view.renderer('painter'); + } + /** @type {import('..').PainterRenderer} */ + const renderer = view._renderer; + // const res = _paint(view.scenegraph(), config); + const { + painterMode, changes, fields, point, radius, range, limits, groupKey='_lab_field', groupValue='_selected', indexKey='_label_index', newColor='' + } = config; + /** @type {'circle' | 'range'} */ + let viewMode; + if (Array.isArray(range)){ + if (range.length >= 2) viewMode = 'circle'; + else viewMode = 'range'; + } + else if (Number.isFinite(range)) viewMode = 'range'; + else throw "Invalid range"; + + const ans = { + mutIndices: new Set(), + mutValues: [], + changes: changes, + view: view, + } + + let items = []; + let test; + + if (viewMode === 'circle') { + test = function(item) { + if (((item.datum[fields[0]] - point[0]) ** 2) / (range[0] ** 2) + + ((item.datum[fields[1]] - point[1]) ** 2) / (range[1] ** 2) <= (radius ** 2)) { + return true; + } + return false; + } + } else if (viewMode === 'range') { + test = function(item) { + const a = (Array.isArray(range)) ? range[0] : range; + // Math.abs(mutData[i][fields[1]] - point[1]) < r * Math.sqrt(range)) + if (item.datum[fields[1]] === point[1] && + (Math.abs(item.datum[fields[0]] - point[0]) < radius * Math.sqrt(a))) { + return true; + } + return false; + } + } + items = renderer.selectItems(view.scenegraph()); + if (painterMode === 'color') { + for (let item of items) if (test(item)) { + item.fill = newColor; + item.datum[groupKey] = groupValue; + ans.mutIndices.add(item.datum[indexKey]); + ans.mutValues.push(item.datum); + } + } + else { + for (let item of items) if (test(item)) { + item.fill = "#ffffff"; + item.opacity = 0; + item.size = 0; + ans.mutIndices.add(item.datum[indexKey]); + // ans.mutValues.push(item.datum); + } + } + renderer.renderAsync(view.scenegraph().root); + + return ans; +} + +/** + * @param {import('vega-typings').View} view + */ +export function startPaint(view) { + view._renderer.isPainting = true; +} +/** + * @param {import('vega-typings').View} view + */ +export function stopPaint(view) { + view._renderer.isPainting = false; +} \ No newline at end of file diff --git a/packages/vega-renderer/src/webgl/LICENSE b/packages/vega-renderer/src/webgl/LICENSE new file mode 100644 index 000000000..f2465c1f7 --- /dev/null +++ b/packages/vega-renderer/src/webgl/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2016, Kitware, Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/vega-renderer/src/webgl/README.md b/packages/vega-renderer/src/webgl/README.md new file mode 100644 index 000000000..5f8f26a4e --- /dev/null +++ b/packages/vega-renderer/src/webgl/README.md @@ -0,0 +1,93 @@ +WebGL renderer for [Vega](https://vega.github.io/vega) +====================================================== + +[Demo](https://vega.github.io/vega-webgl-renderer) + +Major features +-------------- + +* Implements nearly all Vega scene graph components as WebGL primitives. Falls + back to 2D Canvas rendering for text and gradient fills, which are drawn to + a WebGL texture and overlaid on the view. +* Custom shaders for Rect and Symbol marks, enabling greater scalability + (to the hundreds of thousands) beyond Canvas and SVG renderers. See the + "scale" example in the demo (Note: Do not attempt that example with the + SVG renderer, it will lock your browser for a while). + +Usage +----- + +Use this scaffolding to get started using the WebGL renderer. Instead of being +directly usable after loading Vega, as the SVG and Canvas renderers are, +the WebGL renderer is a plugin which requires the inclusion of an additional +JavaScript library. + +The WebGL renderer requires Vega 3.0 (currently in beta). + +```html + + + + + +
+ + +``` + +Build +----- + +``` +npm install +npm run build +``` + +Test +---- + +Testing requires that the Selenium server is running with the Chrome driver installed. +To run all tests: + +``` +npm run test +``` + +To just run the linter: + +``` +npm run lint +``` + +Contributing +------------ + +Please try it out and let us know how it works for you. +Bug reports, feature and improvement ideas, and PRs welcome. + +This code is built and maintained by the [Kitware](http://www.kitware.com) +[Resonant](http://resonant.kitware.com) team in collaboration with the +[UW Interactive Data Lab](https://idl.cs.washington.edu/). The work is supported +by the DARPA AFRL [XDATA](http://opencatalog.darpa.mil/XDATA.html) program. + +Known issues +------------ + +* Line dashes are not supported (see "barley", "nested-plot", "map-bind" examples). +* Triangulation of trails is known to be buggy and leave holes at internal nodes. +* Custom symbol shapes are not supported. +* Triangulation of geometry sometimes results in spurious offshoots. + +Notes +----- + +There is one minor change to [extrude-polyline](https://github.com/mattdesl/extrude-polyline/compare/master...jeffbaumes:closed-path) +which hacks in a way to close mitered strokes, which has not been merged upstream. diff --git a/packages/vega-renderer/src/webgl/WebGLHandler.js b/packages/vega-renderer/src/webgl/WebGLHandler.js new file mode 100644 index 000000000..51100be02 --- /dev/null +++ b/packages/vega-renderer/src/webgl/WebGLHandler.js @@ -0,0 +1,11 @@ +import {CanvasHandler} from 'vega-scenegraph'; +import inherits from './util/inherits'; + +// Patch CanvasHandler +export default function WebGLHandler(loader, tooltip) { + CanvasHandler.call(this, loader, tooltip); +} +var prototype = inherits(WebGLHandler, CanvasHandler); +prototype.context = function() { + return this._canvas.getContext('2d') || this._canvas._textCanvas.getContext('2d'); +}; diff --git a/packages/vega-renderer/src/webgl/WebGLRenderer.js b/packages/vega-renderer/src/webgl/WebGLRenderer.js new file mode 100644 index 000000000..42b960287 --- /dev/null +++ b/packages/vega-renderer/src/webgl/WebGLRenderer.js @@ -0,0 +1,235 @@ +import {Renderer, domClear as clear, Marks as sceneMarks} from 'vega-scenegraph'; +import marks from './marks/index'; +import inherits from './util/inherits'; +import WebGL from './util/webgl'; +import resize from './util/resize'; +import color from './util/color'; +import {drawImage, loadImageAndCreateTextureInfo} from './util/image'; +import {perspective, rotateX, rotateY, rotateZ, multiply, translate as translateGL} from './util/matrix'; + +export default function WebGLRenderer(imageLoader) { + Renderer.call(this, imageLoader); + this._redraw = false; + this._angleX = 0; + this._angleY = 0; + this._angleZ = 0; + this._translateX = 0; + this._translateY = 0; + this._translateZ = 0; + this._zFactor = 0; + this._depthTest = false; + this._randomZ = false; +} + +var prototype = inherits(WebGLRenderer, Renderer), + base = Renderer.prototype; + +prototype.initialize = function(el, width, height, origin) { + this._canvas = WebGL(1, 1); // instantiate a small canvas + if (el) { + clear(el, 0).appendChild(this._canvas); + this._canvas.setAttribute('class', 'marks'); + } + // this method will invoke resize to size the canvas appropriately + return base.initialize.call(this, el, width, height, origin); +}; + +prototype.resize = function(width, height, origin) { + base.resize.call(this, width, height, origin); + resize(this._canvas, this._width, this._height, this._origin); + return this._redraw = true, this; +}; + +prototype.canvas = function() { + return this._canvas; +}; + +prototype.context = function() { + return this._canvas ? ( + this._canvas.getContext('webgl') || + this._canvas.getContext('experimental-webgl')) + : null; +}; + +prototype.rotate = function(x, y, z) { + this._angleX = x; + this._angleY = y; + this._angleZ = z; + return this; +}; + +prototype.translate = function(x, y, z) { + this._translateX = x; + this._translateY = y; + this._translateZ = z; + return this; +}; + +prototype.zFactor = function(z) { + this._zFactor = z; + return this; +}; + +prototype.depthTest = function(val) { + this._depthTest = val; + return this; +}; + +prototype.randomZ = function(val) { + this._randomZ = val; + return this; +}; + +// function clipToBounds(g, items) { +// // TODO: do something here? +// } + +prototype._updateUniforms = function() { + var gl = this.context(); + gl.useProgram(gl._shaderProgram); + + var width = gl.canvas.width / gl._ratio; + var height = gl.canvas.height / gl._ratio; + + var smooshMatrix = [ + 2/width, 0, 0, 0, + 0, -2/width, 0, 0, + 0, 0, 1, 0, + -1, height/width, 0, 1 + ]; + + this.matrix = [ + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + ]; + + this.matrix = multiply(this.matrix, perspective(Math.PI/2, width/height, 0.01, 3000)); + this.matrix = multiply(this.matrix, translateGL(this._translateX, this._translateY, (this._translateZ - 1)*height/width)); + this.matrix = multiply(this.matrix, rotateZ(this._angleZ)); + this.matrix = multiply(this.matrix, rotateY(this._angleY)); + this.matrix = multiply(this.matrix, rotateX(this._angleX)); + this.matrix = multiply(this.matrix, translateGL(0, 0, 1)); + this.matrix = multiply(this.matrix, smooshMatrix); + + gl._matrix = this.matrix; + + gl.uniform1f(gl._zFactorLocation, this._zFactor); + gl.uniformMatrix4fv(gl._matrixLocation, false, this.matrix); +} + +prototype._render = function(scene, items) { + var gl = this.context(), + b, i; + + gl._tx = 0; + gl._ty = 0; + gl._triangleGeometry = []; + gl._triangleColor = []; + if (gl._images) { + for (i = 0; i < gl._images.length; i++) { + gl.deleteTexture(gl._images[i].texture); + } + } + gl._images = []; + gl._randomZ = this._randomZ; + gl._pathCacheHit = 0; + gl._pathCacheMiss = 0; + gl._itemCacheHit = 0; + gl._itemCacheMiss = 0; + gl._shapeCacheHit = 0; + gl._shapeCacheMiss = 0; + + b = (!items || this._redraw) + ? (this._redraw = false, null) + // : clipToBounds(gl, items); + : undefined; + + if (items) { + for (i = 0; i < items.length; i++) { + items[i]._dirty = true; + if (items[i].exit && sceneMarks[items[i].mark.marktype].nested && items[i].mark.items.length) { + // Mark an item as dirty to force redraw of the nested mark + items[i].mark.items[0]._dirty = true; + } + } + } else { + gl._fullRedraw = true; + } + + if (this._depthTest) { + gl.enable(gl.DEPTH_TEST); + } else { + gl.disable(gl.DEPTH_TEST); + } + + this._updateUniforms(); + + this.clear(); + + this.draw(gl, scene, b); + + var imgInfo = loadImageAndCreateTextureInfo(gl, gl._textCanvas); + imgInfo.x = 0; + imgInfo.y = 0; + imgInfo.w = gl.canvas.width / gl._ratio; + imgInfo.h = gl.canvas.height / gl._ratio; + gl._images.push(imgInfo); + + for (i = 0; i < gl._images.length; i++) { + drawImage(gl, gl._images[i], this.matrix); + } + + if (items) { + for (i = 0; i < items.length; i++) { + items[i]._dirty = false; + } + } + gl._fullRedraw = false; + this._lastScene = scene; + + // console.log('Path cache hit: ' + gl._pathCacheHit); + // console.log('Path cache miss: ' + gl._pathCacheMiss); + // console.log('Item cache hit: ' + gl._itemCacheHit); + // console.log('Item cache miss: ' + gl._itemCacheMiss); + // console.log('Shape cache hit: ' + gl._shapeCacheHit); + // console.log('Shape cache miss: ' + gl._shapeCacheMiss); + + return this; +}; + +prototype.frame = function() { + if (this._lastScene) { + this._render(this._lastScene, []); + } + return this; +}; + +prototype.draw = function(ctx, scene, bounds) { + var mark = marks[scene.marktype]; + if (mark.drawGL) { + mark.drawGL.call(this, ctx, scene, bounds); + } +}; + +prototype.toDataURL = function(scene) { + this.render(scene, null); + return this.canvas().toDataURL("image/png", 1); +}; + +prototype.clear = function() { + var gl = this.context(), c; + if (this._bgcolor != null) { + c = color(gl, null, this._bgcolor); + gl.clearColor(c[0], c[1], c[2], 1.0); + } else { + gl.clearColor(1.0, 1.0, 1.0, 1.0); + } + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + + gl._textContext.save(); + gl._textContext.setTransform(1, 0, 0, 1, 0, 0); + gl._textContext.clearRect(0, 0, gl._textCanvas.width, gl._textCanvas.height); + gl._textContext.restore(); +}; diff --git a/packages/vega-renderer/src/webgl/index.js b/packages/vega-renderer/src/webgl/index.js new file mode 100644 index 000000000..d6c1a953c --- /dev/null +++ b/packages/vega-renderer/src/webgl/index.js @@ -0,0 +1,10 @@ +import WebGLHandler from "./WebGLHandler"; +import WebGLRenderer from "./WebGLRenderer"; + +export { WebGLHandler, WebGLRenderer }; + +export const WebGLModule = { + renderer: WebGLRenderer, + headless: WebGLRenderer, + handler: WebGLHandler, +} \ No newline at end of file diff --git a/packages/vega-renderer/src/webgl/marks/arc.js b/packages/vega-renderer/src/webgl/marks/arc.js new file mode 100644 index 000000000..2fc7b4bed --- /dev/null +++ b/packages/vega-renderer/src/webgl/marks/arc.js @@ -0,0 +1,4 @@ +import {arc} from '../path/shapes'; +import markItemPath from './markItemPath'; + +export default markItemPath('arc', arc); diff --git a/packages/vega-renderer/src/webgl/marks/area.js b/packages/vega-renderer/src/webgl/marks/area.js new file mode 100644 index 000000000..03d4dc0cd --- /dev/null +++ b/packages/vega-renderer/src/webgl/marks/area.js @@ -0,0 +1,4 @@ +import {area} from '../path/shapes'; +import markMultiItemPath from './markMultiItemPath'; + +export default markMultiItemPath('area', area); diff --git a/packages/vega-renderer/src/webgl/marks/group.js b/packages/vega-renderer/src/webgl/marks/group.js new file mode 100644 index 000000000..6e59991e9 --- /dev/null +++ b/packages/vega-renderer/src/webgl/marks/group.js @@ -0,0 +1,62 @@ +import {sceneVisit as visit} from 'vega-scenegraph'; +import {rectangleGL} from '../path/shapes'; +import geometryForItem from '../path/geometryForItem'; +import drawGeometry from '../util/drawGeometry'; + +function drawGL(context, scene, bounds) { + var renderer = this; + + visit(scene, function(group) { + var gx = group.x || 0, + gy = group.y || 0, + w = group.width || 0, + h = group.height || 0, + offset, oldClip; + + // setup graphics context + context._tx += gx; + context._ty += gy; + context._textContext.save(); + context._textContext.translate(gx, gy); + + // draw group background + if (context._fullRedraw || group._dirty || !group._geom || group._geom.deleted) { + offset = group.stroke ? 0.5 : 0; + var shapeGeom = rectangleGL(context, group, offset, offset); + group._geom = geometryForItem(context, group, shapeGeom); + } + drawGeometry(group._geom, context, group); + + // set clip and bounds + if (group.clip) { + oldClip = context._clip; + context._clip = [ + context._origin[0] + context._tx, + context._origin[1] + context._ty, + context._origin[0] + context._tx + w, + context._origin[1] + context._ty + h + ]; + } + if (bounds) bounds.translate(-gx, -gy); + + // draw group contents + visit(group, function(item) { + renderer.draw(context, item, bounds); + }); + + // restore graphics context + if (bounds) bounds.translate(gx, gy); + if (group.clip) { + context._clip = oldClip; + } + + context._tx -= gx; + context._ty -= gy; + context._textContext.restore(); + }); +} + +export default { + type: 'group', + drawGL: drawGL +}; diff --git a/packages/vega-renderer/src/webgl/marks/image.js b/packages/vega-renderer/src/webgl/marks/image.js new file mode 100644 index 000000000..b93e97423 --- /dev/null +++ b/packages/vega-renderer/src/webgl/marks/image.js @@ -0,0 +1,53 @@ +import {sceneVisit as visit} from 'vega-scenegraph'; +import {loadImageAndCreateTextureInfo} from '../util/image'; + +function getImage(item, renderer) { + var image = item.image; + if (!image || image.url !== item.url) { + image = {loaded: false, width: 0, height: 0}; + renderer.loadImage(item.url).then(function(image) { + item.image = image; + item.image.url = item.url; + }); + } + return image; +} + +function imageXOffset(align, w) { + return align === 'center' ? w / 2 : align === 'right' ? w : 0; +} + +function imageYOffset(baseline, h) { + return baseline === 'middle' ? h / 2 : baseline === 'bottom' ? h : 0; +} + +function drawGL(context, scene, bounds) { + var renderer = this; + + visit(scene, function(item) { + if (bounds && !bounds.intersects(item.bounds)) return; // bounds check + + var image = getImage(item, renderer), + x = item.x || 0, + y = item.y || 0, + w = item.width || image.width || 0, + h = item.height || image.height || 0; + + x -= imageXOffset(item.align, w); + y -= imageYOffset(item.baseline, h); + + if (image.loaded) { + var imgInfo = loadImageAndCreateTextureInfo(context, image); + imgInfo.x = x + context._tx + context._origin[0]; + imgInfo.y = y + context._ty + context._origin[1]; + imgInfo.w = w; + imgInfo.h = h; + context._images.push(imgInfo); + } + }); +} + +export default { + type: 'image', + drawGL: drawGL +}; diff --git a/packages/vega-renderer/src/webgl/marks/index.js b/packages/vega-renderer/src/webgl/marks/index.js new file mode 100644 index 000000000..4ab088d68 --- /dev/null +++ b/packages/vega-renderer/src/webgl/marks/index.js @@ -0,0 +1,25 @@ +import arc from './arc'; +import area from './area'; +import group from './group'; +import image from './image'; +import line from './line'; +import path from './path'; +import rect from './rect'; +import rule from './rule'; +import shape from './shape'; +import symbol from './symbol'; +import text from './text'; + +export default { + arc: arc, + area: area, + group: group, + image: image, + line: line, + path: path, + rect: rect, + rule: rule, + shape: shape, + symbol: symbol, + text: text +}; diff --git a/packages/vega-renderer/src/webgl/marks/line.js b/packages/vega-renderer/src/webgl/marks/line.js new file mode 100644 index 000000000..81727cb0c --- /dev/null +++ b/packages/vega-renderer/src/webgl/marks/line.js @@ -0,0 +1,4 @@ +import {line} from '../path/shapes'; +import markMultiItemPath from './markMultiItemPath'; + +export default markMultiItemPath('line', line); diff --git a/packages/vega-renderer/src/webgl/marks/markItemPath.js b/packages/vega-renderer/src/webgl/marks/markItemPath.js new file mode 100644 index 000000000..89c7bf1e6 --- /dev/null +++ b/packages/vega-renderer/src/webgl/marks/markItemPath.js @@ -0,0 +1,34 @@ +import {sceneVisit as visit} from 'vega-scenegraph'; +import drawGeometry from '../util/drawGeometry'; +import geometryForItem from '../path/geometryForItem'; + +export default function(type, shape) { + + function drawGL(context, scene, bounds) { + visit(scene, function(item) { + if (bounds && !bounds.intersects(item.bounds)) return; // bounds check + + var x = item.x || 0, + y = item.y || 0, + shapeGeom; + + context._tx += x; + context._ty += y; + + if (context._fullRedraw || item._dirty || !item._geom || item._geom.deleted) { + shapeGeom = shape(context, item); + item._geom = geometryForItem(context, item, shapeGeom); + } + drawGeometry(item._geom, context, item); + + context._tx -= x; + context._ty -= y; + }); + } + + return { + type: type, + drawGL: drawGL + }; + +} diff --git a/packages/vega-renderer/src/webgl/marks/markMultiItemPath.js b/packages/vega-renderer/src/webgl/marks/markMultiItemPath.js new file mode 100644 index 000000000..74f16dc08 --- /dev/null +++ b/packages/vega-renderer/src/webgl/marks/markMultiItemPath.js @@ -0,0 +1,29 @@ +import drawGeometry from '../util/drawGeometry'; +import geometryForItem from '../path/geometryForItem'; + +export default function(type, shape) { + + function drawGL(context, scene, bounds) { + if (scene.items.length && (!bounds || bounds.intersects(scene.bounds))) { + var item = scene.items[0]; + var dirty = false; + for (var i = 0; i < scene.items.length; i++) { + if (scene.items[i]._dirty) { + dirty = true; + break; + } + } + if (context._fullRedraw || dirty || !item._geom || item._geom.deleted) { + var shapeGeom = shape(context, scene.items); + item._geom = geometryForItem(context, item, shapeGeom); + } + drawGeometry(item._geom, context, item); + } + } + + return { + type: type, + drawGL: drawGL + }; + +} diff --git a/packages/vega-renderer/src/webgl/marks/path.js b/packages/vega-renderer/src/webgl/marks/path.js new file mode 100644 index 000000000..03a46cb2f --- /dev/null +++ b/packages/vega-renderer/src/webgl/marks/path.js @@ -0,0 +1,31 @@ +import {sceneVisit as visit} from 'vega-scenegraph'; +import drawGeometry from '../util/drawGeometry'; +import geometryForPath from '../path/geometryForPath'; +import geometryForItem from '../path/geometryForItem'; + +function drawGL(context, scene) { + visit(scene, function(item) { + var path = item.path; + if (path == null) return true; + + var x = item.x || 0, + y = item.y || 0; + + context._tx += x; + context._ty += y; + + if (context._fullRedraw || item._dirty || !item._geom || item._geom.deleted) { + var shapeGeom = geometryForPath(context, path); + item._geom = geometryForItem(context, item, shapeGeom); + } + drawGeometry(item._geom, context, item); + + context._tx -= x; + context._ty -= y; + }); +} + +export default { + type: 'path', + drawGL: drawGL +}; diff --git a/packages/vega-renderer/src/webgl/marks/rect.js b/packages/vega-renderer/src/webgl/marks/rect.js new file mode 100644 index 000000000..c72fb6533 --- /dev/null +++ b/packages/vega-renderer/src/webgl/marks/rect.js @@ -0,0 +1,204 @@ +import {sceneVisit as visit} from 'vega-scenegraph'; +import color from '../util/color'; + +import {Marks as marks} from 'vega-scenegraph'; + +function drawGL(gl, scene, bounds) { + var unit, pos, size, + strokeWidth, strokeOpacity, strokeColor, + fillOpacity, fillColor, cornerRadius, + unitBuffer, posBuffer, sizeBuffer, + strokeWidthBuffer, strokeOpacityBuffer, strokeColorBuffer, + fillOpacityBuffer, fillColorBuffer, cornerRadiusBuffer, + ivpf = 0, ivpf2 = 0, ivpf3 = 0, + numPts = scene.items.length, + unitItem, j, anyGradients = false, ci, + sg = scene._rectGeom; + + for (j = 0; j < scene.items.length; ++j) { + ci = scene.items[j]; + if ((ci.stroke && ci.stroke.id) || (ci.fill && ci.fill.id)) { + anyGradients = true; + break; + } + } + if (anyGradients) { + marks.rect.draw(gl._textContext, scene, bounds); + return; + } + + if (sg && sg.numPts === numPts) { + unit = sg.unit; + unitBuffer = sg.unitBuffer; + pos = sg.pos; + size = sg.size; + strokeWidth = sg.strokeWidth; + strokeOpacity = sg.strokeOpacity; + strokeColor = sg.strokeColor; + fillOpacity = sg.fillOpacity; + fillColor = sg.fillColor; + cornerRadius = sg.cornerRadius; + } else { + if (sg) { + gl.deleteBuffer(sg.unitBuffer); + } + unit = new Float32Array(6 * numPts * 2); + pos = new Float32Array(6 * numPts * 3); + size = new Float32Array(6 * numPts * 2); + strokeWidth = new Float32Array(6 * numPts); + strokeOpacity = new Float32Array(6 * numPts); + strokeColor = new Float32Array(6 * numPts * 3); + fillOpacity = new Float32Array(6 * numPts); + fillColor = new Float32Array(6 * numPts * 3); + cornerRadius = new Float32Array(6 * numPts); + + unitItem = [ + 0, 1, + 1, 1, + 1, 0, + 1, 0, + 0, 0, + 0, 1 + ]; + for (j = 0; j < numPts * unitItem.length; j += 1) { + unit[j] = unitItem[j % unitItem.length]; + } + } + + if (sg) { + gl.deleteBuffer(sg.posBuffer); + gl.deleteBuffer(sg.sizeBuffer); + gl.deleteBuffer(sg.strokeWidthBuffer); + gl.deleteBuffer(sg.strokeOpacityBuffer); + gl.deleteBuffer(sg.strokeColorBuffer); + gl.deleteBuffer(sg.fillOpacityBuffer); + gl.deleteBuffer(sg.fillColorBuffer); + gl.deleteBuffer(sg.cornerRadiusBuffer); + } + + visit(scene, function(item) { + var x = (item.x || 0) + gl._tx + gl._origin[0], + y = (item.y || 0) + gl._ty + gl._origin[1], + fc = color(gl, item, item.fill), + sc = color(gl, item, item.stroke), + op = item.opacity == null ? 1 : item.opacity, + fo = op * ((item.fill == null || item.fill == 'transparent') ? 0 : 1) * (item.fillOpacity == null ? 1 : item.fillOpacity), + so = op * ((item.stroke == null || item.stroke == 'transparent') ? 0 : 1) * (item.strokeOpacity == null ? 1 : item.strokeOpacity), + sw = ((item.stroke == null || item.stroke == 'transparent') ? 0 : 1) * (item.strokeWidth == null ? 1 : item.strokeWidth), + sx = (item.width == null ? 0 : item.width), + sy = (item.height == null ? 0 : item.height), + cr = (item.cornerRadius == null ? 0 : item.cornerRadius); + + for (j = 0; j < 6; j += 1, ivpf += 1, ivpf2 += 2, ivpf3 += 3) { + pos[ivpf3] = x; + pos[ivpf3 + 1] = y; + pos[ivpf3 + 2] = 0; + size[ivpf2] = sx; + size[ivpf2 + 1] = sy; + strokeWidth[ivpf] = sw; + strokeOpacity[ivpf] = so; + strokeColor[ivpf3] = sc[0]; + strokeColor[ivpf3 + 1] = sc[1]; + strokeColor[ivpf3 + 2] = sc[2]; + fillOpacity[ivpf] = fo; + fillColor[ivpf3] = fc[0]; + fillColor[ivpf3 + 1] = fc[1]; + fillColor[ivpf3 + 2] = fc[2]; + cornerRadius[ivpf] = cr; + } + }); + + gl.useProgram(gl._rectShaderProgram); + + if (!unitBuffer) { + unitBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, unitBuffer); + gl.bufferData(gl.ARRAY_BUFFER, unit, gl.STATIC_DRAW); + gl.vertexAttribPointer(gl._rectUnitLocation, 2, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(gl._rectUnitLocation); + } else { + gl.bindBuffer(gl.ARRAY_BUFFER, unitBuffer); + gl.vertexAttribPointer(gl._rectUnitLocation, 2, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(gl._rectUnitLocation); + } + + posBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer); + gl.bufferData(gl.ARRAY_BUFFER, pos, gl.STATIC_DRAW); + gl.vertexAttribPointer(gl._rectPosLocation, 3, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(gl._rectPosLocation); + + sizeBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, sizeBuffer); + gl.bufferData(gl.ARRAY_BUFFER, size, gl.STATIC_DRAW); + gl.vertexAttribPointer(gl._rectSizeLocation, 2, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(gl._rectSizeLocation); + + strokeWidthBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, strokeWidthBuffer); + gl.bufferData(gl.ARRAY_BUFFER, strokeWidth, gl.STATIC_DRAW); + gl.vertexAttribPointer(gl._rectStrokeWidthLocation, 1, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(gl._rectStrokeWidthLocation); + + strokeOpacityBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, strokeOpacityBuffer); + gl.bufferData(gl.ARRAY_BUFFER, strokeOpacity, gl.STATIC_DRAW); + gl.vertexAttribPointer(gl._rectStrokeOpacityLocation, 1, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(gl._rectStrokeOpacityLocation); + + strokeColorBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, strokeColorBuffer); + gl.bufferData(gl.ARRAY_BUFFER, strokeColor, gl.STATIC_DRAW); + gl.vertexAttribPointer(gl._rectStrokeColorLocation, 3, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(gl._rectStrokeColorLocation); + + fillOpacityBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, fillOpacityBuffer); + gl.bufferData(gl.ARRAY_BUFFER, fillOpacity, gl.STATIC_DRAW); + gl.vertexAttribPointer(gl._rectFillOpacityLocation, 1, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(gl._rectFillOpacityLocation); + + fillColorBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, fillColorBuffer); + gl.bufferData(gl.ARRAY_BUFFER, fillColor, gl.STATIC_DRAW); + gl.vertexAttribPointer(gl._rectFillColorLocation, 3, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(gl._rectFillColorLocation); + + cornerRadiusBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, cornerRadiusBuffer); + gl.bufferData(gl.ARRAY_BUFFER, cornerRadius, gl.STATIC_DRAW); + gl.vertexAttribPointer(gl._rectCornerRadiusLocation, 1, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(gl._rectCornerRadiusLocation); + + gl.uniformMatrix4fv(gl._rectMatrixLocation, false, gl._matrix); + gl.uniform4fv(gl._rectClipLocation, gl._clip); + + gl.drawArrays(gl.TRIANGLES, 0, numPts * 6); + + scene._rectGeom = { + numPts: numPts, + unit: unit, + pos: pos, + size: size, + strokeWidth: strokeWidth, + strokeOpacity: strokeOpacity, + strokeColor: strokeColor, + fillOpacity: fillOpacity, + fillColor: fillColor, + cornerRadius: cornerRadius, + unitBuffer: unitBuffer, + posBuffer: posBuffer, + sizeBuffer: sizeBuffer, + strokeWidthBuffer: strokeWidthBuffer, + strokeOpacityBuffer: strokeOpacityBuffer, + strokeColorBuffer: strokeColorBuffer, + fillOpacityBuffer: fillOpacityBuffer, + fillColorBuffer: fillColorBuffer, + cornerRadiusBuffer: cornerRadiusBuffer + }; +} + +export default { + type: 'rect', + drawGL: drawGL +}; diff --git a/packages/vega-renderer/src/webgl/marks/rule.js b/packages/vega-renderer/src/webgl/marks/rule.js new file mode 100644 index 000000000..4c5ce9bc0 --- /dev/null +++ b/packages/vega-renderer/src/webgl/marks/rule.js @@ -0,0 +1,28 @@ +import {sceneVisit as visit} from 'vega-scenegraph'; +import drawGeometry from '../util/drawGeometry'; +import geometryForItem from '../path/geometryForItem'; + +function drawGL(context, scene, bounds) { + visit(scene, function(item) { + var x1, y1, x2, y2, shapeGeom; + if (bounds && !bounds.intersects(item.bounds)) return; // bounds check + if (context._fullRedraw || item._dirty || !item._geom || item._geom.deleted) { + x1 = item.x || 0; + y1 = item.y || 0; + x2 = item.x2 != null ? item.x2 : x1; + y2 = item.y2 != null ? item.y2 : y1; + shapeGeom = { + lines: [[[x1, y1], [x2, y2]]], + closed: false + }; + shapeGeom.key = JSON.stringify(shapeGeom.lines); + item._geom = geometryForItem(context, item, shapeGeom); + } + drawGeometry(item._geom, context, item); + }); +} + +export default { + type: 'rule', + drawGL: drawGL +}; diff --git a/packages/vega-renderer/src/webgl/marks/shape.js b/packages/vega-renderer/src/webgl/marks/shape.js new file mode 100644 index 000000000..f22f36840 --- /dev/null +++ b/packages/vega-renderer/src/webgl/marks/shape.js @@ -0,0 +1,4 @@ +import {shape} from '../path/shapes'; +import markItemPath from './markItemPath'; + +export default markItemPath('shape', shape); diff --git a/packages/vega-renderer/src/webgl/marks/symbol.js b/packages/vega-renderer/src/webgl/marks/symbol.js new file mode 100644 index 000000000..3bd8297ae --- /dev/null +++ b/packages/vega-renderer/src/webgl/marks/symbol.js @@ -0,0 +1,203 @@ +import {sceneVisit as visit} from 'vega-scenegraph'; +import color from '../util/color'; + +function drawGL(gl, scene) { + var unit, pos, size, shape, + strokeWidth, strokeOpacity, strokeColor, + fillOpacity, fillColor, + unitBuffer, posBuffer, sizeBuffer, shapeBuffer, + strokeWidthBuffer, strokeOpacityBuffer, strokeColorBuffer, + fillOpacityBuffer, fillColorBuffer, + ivpf = 0, ivpf3 = 0, + numPts = scene.items.length, + xu = 0, yu = 0, w = 1, h = 1, j, unitItem, + sg = scene._symbolGeom, shapeIndex; + + if (numPts === 0) { + return; + } + + shapeIndex = { + circle: 0, + cross: 1, + diamond: 2, + square: 3, + star: 4, + triangle: 5, + 'triangle-up': 5, + 'triangle-right': 6, + 'triangle-down': 7, + 'triangle-left': 8, + wye: 9 + }; + + if (sg && sg.numPts === numPts) { + unit = sg.unit; + unitBuffer = sg.unitBuffer; + pos = sg.pos; + size = sg.size; + shape = sg.shape; + strokeWidth = sg.strokeWidth; + strokeOpacity = sg.strokeOpacity; + strokeColor = sg.strokeColor; + fillOpacity = sg.fillOpacity; + fillColor = sg.fillColor; + } else { + if (sg) { + gl.deleteBuffer(sg.unitBuffer); + } + unit = new Float32Array(3 * numPts * 2); + pos = new Float32Array(3 * numPts * 3); + size = new Float32Array(3 * numPts); + shape = new Float32Array(3 * numPts); + strokeWidth = new Float32Array(3 * numPts); + strokeOpacity = new Float32Array(3 * numPts); + strokeColor = new Float32Array(3 * numPts * 3); + fillOpacity = new Float32Array(3 * numPts); + fillColor = new Float32Array(3 * numPts * 3); + + unitItem = [ + xu, yu - h * 2, + xu - w * Math.sqrt(3.0), yu + h, + xu + w * Math.sqrt(3.0), yu + h + ]; + for (j = 0; j < numPts * unitItem.length; j += 1) { + unit[j] = unitItem[j % unitItem.length]; + } + } + + if (sg) { + gl.deleteBuffer(sg.posBuffer); + gl.deleteBuffer(sg.sizeBuffer); + gl.deleteBuffer(sg.shapeBuffer); + gl.deleteBuffer(sg.strokeWidthBuffer); + gl.deleteBuffer(sg.strokeOpacityBuffer); + gl.deleteBuffer(sg.strokeColorBuffer); + gl.deleteBuffer(sg.fillOpacityBuffer); + gl.deleteBuffer(sg.fillColorBuffer); + } + + visit(scene, function(item) { + var x = (item.x || 0) + gl._tx + gl._origin[0], + y = (item.y || 0) + gl._ty + gl._origin[1], + fc = color(gl, item, item.fill), + sc = color(gl, item, item.stroke), + op = item.opacity == null ? 1 : item.opacity, + fo = op * ((item.fill == null || item.fill == 'transparent') ? 0 : 1) * (item.fillOpacity == null ? 1 : item.fillOpacity), + so = op * ((item.stroke == null || item.stroke == 'transparent') ? 0 : 1) * (item.strokeOpacity == null ? 1 : item.strokeOpacity), + sw = ((item.stroke == null || item.stroke == 'transparent') ? 0 : 1) * (item.strokeWidth == null ? 1 : item.strokeWidth), + sz = (item.size == null ? 64 : item.size), + sh = shapeIndex[item.shape] == undefined ? 0 : shapeIndex[item.shape]; + + for (j = 0; j < 3; j += 1, ivpf += 1, ivpf3 += 3) { + pos[ivpf3] = x; + pos[ivpf3 + 1] = y; + pos[ivpf3 + 2] = 0; + size[ivpf] = sz; + strokeWidth[ivpf] = sw; + shape[ivpf] = sh; + strokeOpacity[ivpf] = so; + strokeColor[ivpf3] = sc[0]; + strokeColor[ivpf3 + 1] = sc[1]; + strokeColor[ivpf3 + 2] = sc[2]; + fillOpacity[ivpf] = fo; + fillColor[ivpf3] = fc[0]; + fillColor[ivpf3 + 1] = fc[1]; + fillColor[ivpf3 + 2] = fc[2]; + } + }); + + gl.useProgram(gl._symbolShaderProgram); + + if (!unitBuffer) { + unitBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, unitBuffer); + gl.bufferData(gl.ARRAY_BUFFER, unit, gl.STATIC_DRAW); + gl.vertexAttribPointer(gl._symbolUnitLocation, 2, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(gl._symbolUnitLocation); + } else { + gl.bindBuffer(gl.ARRAY_BUFFER, unitBuffer); + gl.vertexAttribPointer(gl._symbolUnitLocation, 2, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(gl._symbolUnitLocation); + } + + posBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer); + gl.bufferData(gl.ARRAY_BUFFER, pos, gl.STATIC_DRAW); + gl.vertexAttribPointer(gl._symbolPosLocation, 3, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(gl._symbolPosLocation); + + sizeBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, sizeBuffer); + gl.bufferData(gl.ARRAY_BUFFER, size, gl.STATIC_DRAW); + gl.vertexAttribPointer(gl._symbolSizeLocation, 1, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(gl._symbolSizeLocation); + + shapeBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, shapeBuffer); + gl.bufferData(gl.ARRAY_BUFFER, shape, gl.STATIC_DRAW); + gl.vertexAttribPointer(gl._symbolShapeLocation, 1, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(gl._symbolShapeLocation); + + strokeWidthBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, strokeWidthBuffer); + gl.bufferData(gl.ARRAY_BUFFER, strokeWidth, gl.STATIC_DRAW); + gl.vertexAttribPointer(gl._symbolStrokeWidthLocation, 1, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(gl._symbolStrokeWidthLocation); + + strokeOpacityBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, strokeOpacityBuffer); + gl.bufferData(gl.ARRAY_BUFFER, strokeOpacity, gl.STATIC_DRAW); + gl.vertexAttribPointer(gl._symbolStrokeOpacityLocation, 1, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(gl._symbolStrokeOpacityLocation); + + strokeColorBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, strokeColorBuffer); + gl.bufferData(gl.ARRAY_BUFFER, strokeColor, gl.STATIC_DRAW); + gl.vertexAttribPointer(gl._symbolStrokeColorLocation, 3, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(gl._symbolStrokeColorLocation); + + fillOpacityBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, fillOpacityBuffer); + gl.bufferData(gl.ARRAY_BUFFER, fillOpacity, gl.STATIC_DRAW); + gl.vertexAttribPointer(gl._symbolFillOpacityLocation, 1, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(gl._symbolFillOpacityLocation); + + fillColorBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, fillColorBuffer); + gl.bufferData(gl.ARRAY_BUFFER, fillColor, gl.STATIC_DRAW); + gl.vertexAttribPointer(gl._symbolFillColorLocation, 3, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(gl._symbolFillColorLocation); + + gl.uniformMatrix4fv(gl._symbolMatrixLocation, false, gl._matrix); + gl.uniform4fv(gl._symbolClipLocation, gl._clip); + + gl.drawArrays(gl.TRIANGLES, 0, numPts * 3); + + scene._symbolGeom = { + numPts: numPts, + unit: unit, + pos: pos, + size: size, + shape: shape, + strokeWidth: strokeWidth, + strokeOpacity: strokeOpacity, + strokeColor: strokeColor, + fillOpacity: fillOpacity, + fillColor: fillColor, + unitBuffer: unitBuffer, + posBuffer: posBuffer, + sizeBuffer: sizeBuffer, + shapeBuffer: shapeBuffer, + strokeWidthBuffer: strokeWidthBuffer, + strokeOpacityBuffer: strokeOpacityBuffer, + strokeColorBuffer: strokeColorBuffer, + fillOpacityBuffer: fillOpacityBuffer, + fillColorBuffer: fillColorBuffer + }; +} + +export default { + type: 'symbol', + drawGL: drawGL +}; diff --git a/packages/vega-renderer/src/webgl/marks/text.js b/packages/vega-renderer/src/webgl/marks/text.js new file mode 100644 index 000000000..48acea5bd --- /dev/null +++ b/packages/vega-renderer/src/webgl/marks/text.js @@ -0,0 +1,10 @@ +import {Marks as marks} from 'vega-scenegraph'; + +function drawGL(context, scene, bounds) { + marks.text.draw(context._textContext, scene, bounds); +} + +export default { + type: 'text', + drawGL: drawGL +}; diff --git a/packages/vega-renderer/src/webgl/path/geometryForItem.js b/packages/vega-renderer/src/webgl/path/geometryForItem.js new file mode 100644 index 000000000..5d5289f51 --- /dev/null +++ b/packages/vega-renderer/src/webgl/path/geometryForItem.js @@ -0,0 +1,130 @@ +import color from '../util/color'; +import extrude from 'extrude-polyline'; + +export default function(context, item, shapeGeom) { + var lw = (lw = item.strokeWidth) != null ? lw : 1, + lc = (lc = item.strokeCap) != null ? lc : 'butt'; + var strokeMeshes = []; + var i, len, c, li, ci, mesh, triangles = [], colors = [], cell, p1, p2, p3, mp, mc, mcl, + triangleBuffer, colorBuffer, n = 0, fill = false, stroke = false; + var opacity = item.opacity == null ? 1 : item.opacity; + var fillOpacity = opacity * (item.fillOpacity==null ? 1 : item.fillOpacity); + var strokeOpacity = opacity * (item.strokeOpacity==null ? 1 : item.strokeOpacity), + strokeExtrude, + z = shapeGeom.z || 0, + st = shapeGeom.triangles, + val; + + if (item.fill === 'transparent') { + fillOpacity = 0; + } + if (item.fill && fillOpacity > 0) { + fill = true; + n = st ? st.length / 9 : 0; + } + + if (item.stroke === 'transparent') { + strokeOpacity = 0; + } + if (lw > 0 && item.stroke && strokeOpacity > 0) { + stroke = true; + strokeExtrude = extrude({ + thickness: lw, + cap: lc, + join: 'miter', + miterLimit: 1, + closed: !!shapeGeom.closed + }); + for (li = 0; li < shapeGeom.lines.length; li++) { + mesh = strokeExtrude.build(shapeGeom.lines[li]); + strokeMeshes.push(mesh); + n += mesh.cells.length; + } + } + + triangles = new Float32Array(n * 3 * 3); + colors = new Float32Array(n * 3 * 4); + + if (fill) { + c = color(context, item, item.fill); + for (i = 0, len = st.length; i < len; i += 3) { + triangles[i ] = st[i ]; + triangles[i + 1] = st[i + 1]; + triangles[i + 2] = st[i + 2]; + } + for (i = 0, len = st.length / 3; i < len; i++) { + colors[i*4 ] = c[0]; + colors[i*4 + 1] = c[1]; + colors[i*4 + 2] = c[2]; + colors[i*4 + 3] = fillOpacity; + } + } + + if (stroke) { + c = color(context, item, item.stroke); + i = fill ? st.length / 3 : 0; + for (li = 0; li < strokeMeshes.length; li++) { + mesh = strokeMeshes[li], + mp = mesh.positions, + mc = mesh.cells, + mcl = mesh.cells.length; + for (ci = 0; ci < mcl; ci++) { + cell = mc[ci]; + p1 = mp[cell[0]]; + p2 = mp[cell[1]]; + p3 = mp[cell[2]]; + triangles[i*3 ] = p1[0]; + triangles[i*3 + 1] = p1[1]; + triangles[i*3 + 2] = z; + colors[i*4 ] = c[0]; + colors[i*4 + 1] = c[1]; + colors[i*4 + 2] = c[2]; + colors[i*4 + 3] = strokeOpacity; + i++; + + triangles[i*3 ] = p2[0]; + triangles[i*3 + 1] = p2[1]; + triangles[i*3 + 2] = z; + colors[i*4 ] = c[0]; + colors[i*4 + 1] = c[1]; + colors[i*4 + 2] = c[2]; + colors[i*4 + 3] = strokeOpacity; + i++; + + triangles[i*3 ] = p3[0]; + triangles[i*3 + 1] = p3[1]; + triangles[i*3 + 2] = z; + colors[i*4 ] = c[0]; + colors[i*4 + 1] = c[1]; + colors[i*4 + 2] = c[2]; + colors[i*4 + 3] = strokeOpacity; + i++; + } + } + } + + triangleBuffer = context.createBuffer(); + context.bindBuffer(context.ARRAY_BUFFER, triangleBuffer); + context.bufferData(context.ARRAY_BUFFER, triangles, context.STATIC_DRAW); + + colorBuffer = context.createBuffer(); + context.bindBuffer(context.ARRAY_BUFFER, colorBuffer); + context.bufferData(context.ARRAY_BUFFER, colors, context.STATIC_DRAW); + + val = { + triangleBuffer: triangleBuffer, + colorBuffer: colorBuffer, + numTriangles: n + }; + + if (item._geom) { + if (item._geom.triangleBuffer) { + context.deleteBuffer(item._geom.triangleBuffer); + } + if (item._geom.colorBuffer) { + context.deleteBuffer(item._geom.colorBuffer); + } + } + + return val; +} diff --git a/packages/vega-renderer/src/webgl/path/geometryForPath.js b/packages/vega-renderer/src/webgl/path/geometryForPath.js new file mode 100644 index 000000000..5f071ca2e --- /dev/null +++ b/packages/vega-renderer/src/webgl/path/geometryForPath.js @@ -0,0 +1,67 @@ +import parse from 'parse-svg-path'; +import simplify from 'simplify-path'; +import contours from 'svg-path-contours'; +import triangulate from 'triangulate-contours'; + +export default function(context, path, threshold) { + var key = path; + if (context._pathCache[key]) { + context._pathCacheHit++; + return context._pathCache[key]; + } + context._pathCacheMiss++; + + threshold = threshold || 1.0; + if (!path) { + return {lines: [], triangles: [], closed: false, z: 0}; + } + + // get a list of polylines/contours from svg contents + var lines = contours(parse(path)), tri; + + // simplify the contours before triangulation + lines = lines.map(function(path) { + return simplify(path, threshold); + }); + + // triangluate can fail in some corner cases + try { + tri = triangulate(lines); + } + catch(e) { + // console.log('Could not triangulate the following path:'); + // console.log(path); + // console.log(e); + tri = {positions: [], cells: []}; + } + + var z = context._randomZ ? 0.25*(Math.random() - 0.5) : 0; + + var triangles = []; + var tcl = tri.cells.length, + tc = tri.cells, + tp = tri.positions; + for (var ci = 0; ci < tcl; ci++) { + var cell = tc[ci]; + var p1 = tp[cell[0]]; + var p2 = tp[cell[1]]; + var p3 = tp[cell[2]]; + triangles.push(p1[0], p1[1], z, p2[0], p2[1], z, p3[0], p3[1], z); + } + + var geom = { + lines: lines, + triangles: triangles, + closed: path.endsWith('Z'), + z: z, + key: key + }; + + context._pathCache[key] = geom; + context._pathCacheSize++; + if (context._pathCacheSize > 10000) { + context._pathCache = {}; + context._pathCacheSize = 0; + } + return geom; +} diff --git a/packages/vega-renderer/src/webgl/path/geometryForShape.js b/packages/vega-renderer/src/webgl/path/geometryForShape.js new file mode 100644 index 000000000..355a52bc5 --- /dev/null +++ b/packages/vega-renderer/src/webgl/path/geometryForShape.js @@ -0,0 +1,32 @@ +import geometryForItem from './geometryForItem'; + +export default function(context, item, shape, keyFunc) { + var key = keyFunc(item), shapeGeom, val, v; + + if (context._shapeCache[key]) { + context._shapeCacheHit++; + return context._shapeCache[key]; + } + context._shapeCacheMiss++; + + shapeGeom = shape(context, item); + val = geometryForItem(context, item, shapeGeom); + + if (context._shapeCacheSize > 10000) { + for (v in context._shapeCache) { + if (context._shapeCache.hasOwnProperty(v)) { + context.deleteBuffer(context._shapeCache[v].triangleBuffer); + context.deleteBuffer(context._shapeCache[v].colorBuffer); + context._shapeCache[v].deleted = true; + } + } + context._lastColorBuffer = null; + context._lastTriangleBuffer = null; + context._shapeCache = {}; + context._shapeCacheSize = 0; + } + context._shapeCache[key] = val; + context._shapeCacheSize++; + + return val; +} diff --git a/packages/vega-renderer/src/webgl/path/shapes.js b/packages/vega-renderer/src/webgl/path/shapes.js new file mode 100644 index 000000000..53de2e43f --- /dev/null +++ b/packages/vega-renderer/src/webgl/path/shapes.js @@ -0,0 +1,85 @@ +import {pathCurves, pathSymbols, pathRectangle, pathTrail} from 'vega-scenegraph'; + +import geometryForPath from './geometryForPath'; + +import { + arc as d3_arc, + symbol as d3_symbol, + area as d3_area, + line as d3_line +} from 'd3-shape'; + +function x(item) { return item.x || 0; } +function y(item) { return item.y || 0; } +function w(item) { return item.width || 0; } +function wh(item) { return item.width || item.height || 1; } +function h(item) { return item.height || 0; } +function xw(item) { return (item.x || 0) + (item.width || 0); } +function yh(item) { return (item.y || 0) + (item.height || 0); } +function cr(item) { return item.cornerRadius || 0; } +function pa(item) { return item.padAngle || 0; } +function def(item) { return !(item.defined === false); } +function size(item) { return item.size == null ? 64 : item.size; } +function type(item) { return pathSymbols(item.shape || 'circle'); } + +var arcShape = d3_arc().cornerRadius(cr).padAngle(pa), + areavShape = d3_area().x(x).y1(y).y0(yh).defined(def), + areahShape = d3_area().y(y).x1(x).x0(xw).defined(def), + lineShape = d3_line().x(x).y(y).defined(def), + trailShape = pathTrail().x(x).y(y).defined(def).size(wh), + rectShape = pathRectangle().x(x).y(y).width(w).height(h).cornerRadius(cr), + rectShapeGL = pathRectangle().x(0).y(0).width(w).height(h).cornerRadius(cr), + symbolShape = d3_symbol().type(type).size(size); + +export function arc(context, item) { + if (!context || context.arc) { + return arcShape.context(context)(item); + } + return geometryForPath(context, arcShape.context(null)(item), 0.1); +} + +export function area(context, items) { + var item = items[0], + interp = item.interpolate || 'linear', + s = (interp === 'trail' ? trailShape + : (item.orient === 'horizontal' ? areahShape : areavShape) + .curve(pathCurves(interp, item.orient, item.tension)) + ) + if (!context || context.arc) { + return s.context(context)(items); + } + return geometryForPath(context, s.context(null)(items), 0.1); +} + +export function shape(context, item) { + var s = item.mark.shape || item.shape; + if (!context || context.arc) { + return s.context(context)(item); + } + return geometryForPath(context, s.context(null)(item), 0.1); +} + +export function line(context, items) { + var item = items[0], + interp = item.interpolate || 'linear', + s = lineShape.curve(pathCurves(interp, item.orient, item.tension)); + if (!context || context.arc) { + return s.context(context)(items); + } + return geometryForPath(context, s.context(null)(items)); +} + +export function rectangle(context, item, x, y) { + return rectShape.context(context)(item, x, y); +} + +export function rectangleGL(context, item, x, y) { + return geometryForPath(context, rectShapeGL.context(null)(item, x, y), 0.1); +} + +export function symbol(context, item) { + if (!context || context.arc) { + return symbolShape.context(context)(item); + } + return geometryForPath(context, symbolShape.context(null)(item), 0.1); +} diff --git a/packages/vega-renderer/src/webgl/util/color.js b/packages/vega-renderer/src/webgl/util/color.js new file mode 100644 index 000000000..f3a4e6157 --- /dev/null +++ b/packages/vega-renderer/src/webgl/util/color.js @@ -0,0 +1,19 @@ +import {color} from 'd3-color'; + +var cache = {}; + +export default function(context, item, value) { + if (!value) { + return [1.0, 1.0, 1.0]; + } + if (value.id) { + // TODO: support gradients + return [1.0, 1.0, 1.0]; + } + if (cache[value]) { + return cache[value]; + } + var rgb = color(value).rgb(); + cache[value] = [rgb.r / 255, rgb.g / 255, rgb.b / 255]; + return cache[value]; +} diff --git a/packages/vega-renderer/src/webgl/util/drawGeometry.js b/packages/vega-renderer/src/webgl/util/drawGeometry.js new file mode 100644 index 000000000..50cfa9fd0 --- /dev/null +++ b/packages/vega-renderer/src/webgl/util/drawGeometry.js @@ -0,0 +1,25 @@ +export default function(geom, gl, item) { + var opacity = item.opacity == null ? 1 : item.opacity, + tx = gl._tx + gl._origin[0], + ty = gl._ty + gl._origin[1]; + + if (opacity <= 0) return; + if (geom.numTriangles === 0) return; + + gl.useProgram(gl._shaderProgram); + + gl.bindBuffer(gl.ARRAY_BUFFER, geom.triangleBuffer); + gl.vertexAttribPointer(gl._coordLocation, 3, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(gl._coordLocation); + gl._lastTriangleBuffer = geom.triangleBuffer; + + gl.bindBuffer(gl.ARRAY_BUFFER, geom.colorBuffer); + gl.vertexAttribPointer(gl._colorLocation, 4, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(gl._colorLocation); + gl._lastColorBuffer = geom.colorBuffer; + + gl.uniform2fv(gl._offsetLocation, [tx, ty]); + gl.uniform4fv(gl._clipLocation, gl._clip); + + gl.drawArrays(gl.TRIANGLES, 0, geom.numTriangles * 3); +} diff --git a/packages/vega-renderer/src/webgl/util/image.js b/packages/vega-renderer/src/webgl/util/image.js new file mode 100644 index 000000000..f8e63a073 --- /dev/null +++ b/packages/vega-renderer/src/webgl/util/image.js @@ -0,0 +1,74 @@ +import {multiply} from './matrix'; + +// Adapted from https://github.com/greggman/webgl-fundamentals +// +// BSD license follows: +// +// Copyright 2012, Gregg Tavares. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Gregg Tavares. nor the names of his +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// creates a texture info { width: w, height: h, texture: tex } +export function loadImageAndCreateTextureInfo(gl, img) { + var tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + // Fill the texture with a 1x1 blue pixel. + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, + new Uint8Array([0, 0, 255, 255])); + // let's assume all images are not a power of 2 + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + var textureInfo = {texture: tex}; + textureInfo.width = img.width; + textureInfo.height = img.height; + gl.bindTexture(gl.TEXTURE_2D, textureInfo.texture); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img); + return textureInfo; +} + +export function drawImage(gl, texInfo, matrix) { + gl.bindTexture(gl.TEXTURE_2D, texInfo.texture); + gl.useProgram(gl._imageShaderProgram); + gl.bindBuffer(gl.ARRAY_BUFFER, gl._imagePositionBuffer); + gl.enableVertexAttribArray(gl._imagePositionLocation); + gl.vertexAttribPointer(gl._imagePositionLocation, 2, gl.FLOAT, false, 0, 0); + gl.bindBuffer(gl.ARRAY_BUFFER, gl._imageTexcoordBuffer); + gl.enableVertexAttribArray(gl._imageTexcoordLocation); + gl.vertexAttribPointer(gl._imageTexcoordLocation, 2, gl.FLOAT, false, 0, 0); + var preMatrix = [ + texInfo.w, 0, 0, 0, + 0, texInfo.h, 0, 0, + 0, 0, 1, 0, + texInfo.x, texInfo.y, 0, 1 + ]; + matrix = multiply(matrix, preMatrix); + gl.uniformMatrix4fv(gl._imageMatrixLocation, false, matrix); + gl.uniform1i(gl._imageTextureLocation, 0); + gl.drawArrays(gl.TRIANGLES, 0, 6); +} diff --git a/packages/vega-renderer/src/webgl/util/inherits.js b/packages/vega-renderer/src/webgl/util/inherits.js new file mode 100644 index 000000000..2817856b1 --- /dev/null +++ b/packages/vega-renderer/src/webgl/util/inherits.js @@ -0,0 +1,5 @@ +export default function(child, parent) { + var proto = (child.prototype = Object.create(parent.prototype)); + proto.constructor = child; + return proto; +} diff --git a/packages/vega-renderer/src/webgl/util/matrix.js b/packages/vega-renderer/src/webgl/util/matrix.js new file mode 100644 index 000000000..20b31a4f3 --- /dev/null +++ b/packages/vega-renderer/src/webgl/util/matrix.js @@ -0,0 +1,79 @@ + +export function multiply(a, b) { + var ind = [ + 0,0,4,1,8,2,12,3, + 1,0,5,1,9,2,13,3, + 2,0,6,1,10,2,14,3, + 3,0,7,1,11,2,15,3, + 0,4,4,5,8,6,12,7, + 1,4,5,5,9,6,13,7, + 2,4,6,5,10,6,14,7, + 3,4,7,5,11,6,15,7, + 0,8,4,9,8,10,12,11, + 1,8,5,9,9,10,13,11, + 2,8,6,9,10,10,14,11, + 3,8,7,9,11,10,15,11, + 0,12,4,13,8,14,12,15, + 1,12,5,13,9,14,13,15, + 2,12,6,13,10,14,14,15, + 3,12,7,13,11,14,15,15 + ]; + var c = [ + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 + ]; + for (var i = 0; i < ind.length/2; i++) { + c[Math.floor(i/4)] += a[ind[2*i]] * b[ind[2*i+1]]; + } + return c; +} + +export function perspective(fieldOfViewInRadians, aspect, near, far) { + var f = Math.tan(Math.PI * 0.5 - 0.5 * fieldOfViewInRadians); + var rangeInv = 1.0 / (near - far); + + return [ + f / aspect, 0, 0, 0, + 0, f, 0, 0, + 0, 0, (near + far) * rangeInv, -1, + 0, 0, near * far * rangeInv * 2, 0 + ]; +} + +export function rotateX(a) { + return [ + 1, 0, 0, 0, + 0, Math.cos(a), Math.sin(a), 0, + 0, -Math.sin(a), Math.cos(a), 0, + 0, 0, 0, 1 + ]; +} + +export function rotateY(a) { + return [ + Math.cos(a), 0, -Math.sin(a), 0, + 0, 1, 0, 0, + Math.sin(a), 0, Math.cos(a), 0, + 0, 0, 0, 1 + ]; +} + +export function rotateZ(a) { + return [ + Math.cos(a), Math.sin(a), 0, 0, + -Math.sin(a), Math.cos(a), 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + ]; +} + +export function translate(x, y, z) { + return [ + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + x, y, z, 1 + ]; +} diff --git a/packages/vega-renderer/src/webgl/util/resize.js b/packages/vega-renderer/src/webgl/util/resize.js new file mode 100644 index 000000000..3154877ee --- /dev/null +++ b/packages/vega-renderer/src/webgl/util/resize.js @@ -0,0 +1,38 @@ +export var devicePixelRatio = typeof window !== 'undefined' + ? window.devicePixelRatio || 1 : 1; + +export default function(canvas, width, height, origin) { + var scale = typeof HTMLElement !== 'undefined' + && canvas instanceof HTMLElement + && canvas.parentNode != null; + + var gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl'), + ratio = scale ? devicePixelRatio : 1; + + canvas.width = width * ratio; + canvas.height = height * ratio; + + gl._textCanvas.width = width * ratio; + gl._textCanvas.height = height * ratio; + gl._textContext.pixelRatio = ratio; + gl._textContext.setTransform( + ratio, 0, 0, ratio, + ratio * origin[0], + ratio * origin[1] + ); + + if (ratio !== 1) { + canvas.style.width = width + 'px'; + canvas.style.height = height + 'px'; + } + + gl.lineWidth(ratio); + + gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); + + gl._origin = origin; + gl._ratio = ratio; + gl._clip = [0, 0, canvas.width / gl._ratio, canvas.height / gl._ratio]; + + return canvas; +} diff --git a/packages/vega-renderer/src/webgl/util/webgl.js b/packages/vega-renderer/src/webgl/util/webgl.js new file mode 100644 index 000000000..82d3457a9 --- /dev/null +++ b/packages/vega-renderer/src/webgl/util/webgl.js @@ -0,0 +1,662 @@ +export var WebGL; + +export default function(w, h) { + var canvas = document.createElement('canvas'); + canvas.width = w; + canvas.height = h; + + var gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); + + gl._pathCache = {}; + gl._pathCacheSize = 0; + gl._itemCache = {}; + gl._itemCacheSize = 0; + gl._shapeCache = {}; + gl._shapeCacheSize = 0; + + gl._textCanvas = document.createElement('canvas'); + gl._textCanvas.width = w; + gl._textCanvas.height = h; + gl._textContext = gl._textCanvas.getContext('2d'); + canvas._textCanvas = gl._textCanvas; + + gl.clearColor(1.0, 1.0, 1.0, 1.0); + gl.disable(gl.CULL_FACE); + + // Thanks to https://limnu.com/webgl-blending-youre-probably-wrong/ + gl.disable(gl.DEPTH_TEST); + gl.enable(gl.BLEND); + gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA); + + var vertCode = + 'attribute vec3 coordinates;' + + 'attribute vec4 color;' + + 'uniform mat4 matrix;' + + 'uniform float zFactor;' + + 'uniform vec2 offset;' + + 'varying vec4 vColor;' + + 'varying vec4 vPosition;' + + 'void main(void) {' + + ' vPosition = vec4(coordinates.x + offset.x, coordinates.y + offset.y, coordinates.z*zFactor - 1.0, 1.0);' + + ' gl_Position = matrix * vPosition;' + + ' vColor = color;' + + '}'; + var vertShader = gl.createShader(gl.VERTEX_SHADER); + gl.shaderSource(vertShader, vertCode); + gl.compileShader(vertShader); + if (!gl.getShaderParameter(vertShader, gl.COMPILE_STATUS)) { + throw gl.getShaderInfoLog(vertShader); + } + + var fragCode = + 'precision mediump float;' + + 'varying vec4 vColor;' + + 'varying vec4 vPosition;' + + 'uniform vec4 clip;' + + 'void main(void) {' + + ' if (vPosition.x < clip[0] || vPosition.x > clip[2] || vPosition.y < clip[1] || vPosition.y > clip[3]) {' + + ' discard;' + + ' }' + + ' gl_FragColor = vColor;' + + '}'; + var fragShader = gl.createShader(gl.FRAGMENT_SHADER); + gl.shaderSource(fragShader, fragCode); + gl.compileShader(fragShader); + if (!gl.getShaderParameter(fragShader, gl.COMPILE_STATUS)) { + throw gl.getShaderInfoLog(fragShader); + } + + var shaderProgram = gl.createProgram(); + gl.attachShader(shaderProgram, vertShader); + gl.attachShader(shaderProgram, fragShader); + gl.linkProgram(shaderProgram); + gl.useProgram(shaderProgram); + gl._shaderProgram = shaderProgram; + gl._coordLocation = gl.getAttribLocation(gl._shaderProgram, 'coordinates'); + gl._colorLocation = gl.getAttribLocation(gl._shaderProgram, 'color'); + gl._matrixLocation = gl.getUniformLocation(gl._shaderProgram, 'matrix'); + gl._zFactorLocation = gl.getUniformLocation(gl._shaderProgram, 'zFactor'); + gl._offsetLocation = gl.getUniformLocation(gl._shaderProgram, 'offset'); + gl._clipLocation = gl.getUniformLocation(gl._shaderProgram, 'clip'); + +// ------------------------------------------------------------------------- +// BEGIN: Adapted from https://github.com/greggman/webgl-fundamentals +// +// BSD license follows: +// +// Copyright 2012, Gregg Tavares. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Gregg Tavares. nor the names of his +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + vertCode = + 'attribute vec2 a_position;' + + 'attribute vec2 a_texcoord;' + + 'uniform mat4 u_matrix;' + + 'varying vec2 v_texcoord;' + + 'void main() {' + + ' gl_Position = u_matrix * vec4(a_position, -1.0, 1.0);' + + ' v_texcoord = a_texcoord;' + + '}'; + vertShader = gl.createShader(gl.VERTEX_SHADER); + gl.shaderSource(vertShader, vertCode); + gl.compileShader(vertShader); + if (!gl.getShaderParameter(vertShader, gl.COMPILE_STATUS)) { + throw gl.getShaderInfoLog(vertShader); + } + + fragCode = + 'precision mediump float;' + + 'varying vec2 v_texcoord;' + + 'uniform sampler2D u_texture;' + + 'void main() {' + + ' gl_FragColor = texture2D(u_texture, v_texcoord);' + + '}'; + fragShader = gl.createShader(gl.FRAGMENT_SHADER); + gl.shaderSource(fragShader, fragCode); + gl.compileShader(fragShader); + if (!gl.getShaderParameter(fragShader, gl.COMPILE_STATUS)) { + throw gl.getShaderInfoLog(fragShader); + } + + shaderProgram = gl.createProgram(); + gl.attachShader(shaderProgram, vertShader); + gl.attachShader(shaderProgram, fragShader); + gl.linkProgram(shaderProgram); + + var positionBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); + var positions = [ + 0, 0, + 0, 1, + 1, 0, + 1, 0, + 0, 1, + 1, 1 + ]; + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW); + + var texcoordBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer); + var texcoords = [ + 0, 0, + 0, 1, + 1, 0, + 1, 0, + 0, 1, + 1, 1 + ]; + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(texcoords), gl.STATIC_DRAW); + + gl._imageShaderProgram = shaderProgram; + gl._imagePositionLocation = gl.getAttribLocation(gl._imageShaderProgram, 'a_position'); + gl._imageTexcoordLocation = gl.getAttribLocation(gl._imageShaderProgram, 'a_texcoord'); + gl._imageMatrixLocation = gl.getUniformLocation(gl._imageShaderProgram, 'u_matrix'); + gl._imageTextureLocation = gl.getUniformLocation(gl._imageShaderProgram, 'u_texture'); + gl._imagePositionBuffer = positionBuffer; + gl._imageTexcoordBuffer = texcoordBuffer; + +// END: Adapted from https://github.com/greggman/webgl-fundamentals +// ------------------------------------------------------------------------- + + vertCode = [ + 'attribute vec3 pos;', + 'attribute vec3 fillColor;', + 'attribute vec3 strokeColor;', + 'attribute float fillOpacity;', + 'attribute float strokeWidth;', + 'attribute float size;', + 'attribute float shape;', + 'attribute float strokeOpacity;', + 'attribute vec2 unit;', + 'uniform mat4 matrix;', + 'varying vec4 positionVar;', + 'varying vec4 fillColorVar;', + 'varying vec4 strokeColorVar;', + 'varying float sizeVar;', + 'varying float shapeVar;', + 'varying float strokeWidthVar;', + 'varying vec2 unitVar;', + 'void main(void)', + '{', + ' strokeWidthVar = strokeWidth;', + ' fillColorVar = vec4(fillColor, fillOpacity);', + ' strokeColorVar = vec4(strokeColor, strokeOpacity);', + + // circle + ' if (shape == 0.0) {', + ' sizeVar = sqrt(size) / 2.0;', + ' }', + + // cross + ' if (shape == 1.0) {', + ' sizeVar = sqrt(size) / 2.0;', + ' }', + + // diamond + ' if (shape == 2.0) {', + ' sizeVar = sqrt(size) / 2.0;', + ' }', + + // square + ' if (shape == 3.0) {', + ' sizeVar = sqrt(2.0) * sqrt(size) / 2.0;', + ' }', + + // star + ' if (shape == 4.0) {', + ' sizeVar = sqrt(size) / 2.0;', + ' }', + + // triangle-up + ' if (shape == 5.0) {', + ' sizeVar = (sqrt(6.0)/2.0) * sqrt(size) / 2.0;', + ' }', + + // triangle-right + ' if (shape == 6.0) {', + ' sizeVar = (sqrt(6.0)/2.0) * sqrt(size) / 2.0;', + ' }', + + // triangle-down + ' if (shape == 7.0) {', + ' sizeVar = (sqrt(6.0)/2.0) * sqrt(size) / 2.0;', + ' }', + + // triangle-left + ' if (shape == 8.0) {', + ' sizeVar = (sqrt(6.0)/2.0) * sqrt(size) / 2.0;', + ' }', + + // wye + ' if (shape == 9.0) {', + ' sizeVar = sqrt(size) / 2.0;', + ' }', + + // ' sizeVar = size;', + ' shapeVar = shape;', + // ' float m = size + strokeWidth;', + ' float factor = (sizeVar + strokeWidth / 2.0 + 1.0) / sizeVar;', + ' unitVar = factor * unit;', + ' positionVar = vec4(pos.xy + factor * sizeVar * unit, -1.0, 1.0);', + ' gl_Position = matrix * positionVar;', + '}' + ].join('\n'); + vertShader = gl.createShader(gl.VERTEX_SHADER); + gl.shaderSource(vertShader, vertCode); + gl.compileShader(vertShader); + if (!gl.getShaderParameter(vertShader, gl.COMPILE_STATUS)) { + throw gl.getShaderInfoLog(vertShader); + } + + fragCode = [ + 'precision mediump float;', + 'const float PI = 3.1415926535897932384626433832795;', + 'varying vec4 positionVar;', + 'varying vec4 fillColorVar;', + 'varying vec4 strokeColorVar;', + 'varying vec2 unitVar;', + 'varying float sizeVar;', + 'varying float shapeVar;', + 'varying float strokeWidthVar;', + 'uniform vec4 clip;', + + 'float distToLine(vec2 pt1, vec2 pt2, vec2 testPt)', + '{', + ' vec2 lineDir = pt2 - pt1;', + ' vec2 perpDir = vec2(lineDir.y, -lineDir.x);', + ' vec2 dirToPt1 = pt1 - testPt;', + ' return dot(normalize(perpDir), dirToPt1);', + '}', + + 'float distToAngle(vec2 apex, vec2 left, vec2 right, vec2 testPt)', + '{', + ' float dist = distToLine(apex, left, testPt);', + ' dist = min(dist, distToLine(right, apex, testPt));', + ' float cut = distToLine(left, right, testPt);', + ' if (cut < 0.0) return -1.0;', + ' return dist;', + '}', + + 'float distToHull(vec2 p1, vec2 p2, vec2 p3, vec2 p4, vec2 testPt)', + '{', + ' float dist = distToLine(p1, p2, testPt);', + ' dist = min(dist, distToLine(p2, p3, testPt));', + ' dist = min(dist, distToLine(p4, p1, testPt));', + ' float cut = distToLine(p3, p4, testPt);', + ' if (cut < 0.0) return -1.0;', + ' return dist;', + '}', + + 'vec2 rotate(vec2 pt, float a)', + '{', + ' return vec2(cos(a)*pt.x - sin(a)*pt.y, sin(a)*pt.x + cos(a)*pt.y);', + '}', + + 'void main () {', + ' if (positionVar.x < clip[0] || positionVar.x > clip[2] || positionVar.y < clip[1] || positionVar.y > clip[3]) {', + ' discard;', + ' }', + + ' float dist;', + ' float d1;', + ' float d2;', + + // circle + ' if (shapeVar == 0.0) {', + ' dist = length(unitVar);', + ' }', + + // cross + ' if (shapeVar == 1.0) {', + ' float inset = 1.0 / 2.5;', + ' d1 = distToLine(vec2(-inset, -1.0), vec2(inset, -1.0), unitVar);', + ' d1 = min(d1, distToLine(vec2(inset, -1.0), vec2(inset, 1.0), unitVar));', + ' d1 = min(d1, distToLine(vec2(inset, 1.0), vec2(-inset, 1.0), unitVar));', + ' d1 = min(d1, distToLine(vec2(-inset, 1.0), vec2(-inset, -1.0), unitVar));', + ' d1 = 1.0 - d1;', + ' d2 = distToLine(vec2(1.0, -inset), vec2(1.0, inset), unitVar);', + ' d2 = min(d2, distToLine(vec2(1.0, inset), vec2(-1.0, inset), unitVar));', + ' d2 = min(d2, distToLine(vec2(-1.0, inset), vec2(-1.0, -inset), unitVar));', + ' d2 = min(d2, distToLine(vec2(-1.0, -inset), vec2(1.0, -inset), unitVar));', + ' d2 = 1.0 - d2;', + ' dist = min(d1, d2);', + ' }', + + // diamond + ' if (shapeVar == 2.0) {', + ' dist = distToLine(vec2(0.0, -1.0), vec2(1.0, 0.0), unitVar);', + ' dist = min(dist, distToLine(vec2(1.0, 0.0), vec2(0.0, 1.0), unitVar));', + ' dist = min(dist, distToLine(vec2(0.0, 1.0), vec2(-1.0, 0.0), unitVar));', + ' dist = min(dist, distToLine(vec2(-1.0, 0.0), vec2(0.0, -1.0), unitVar));', + ' dist = 1.0 - dist;', + ' }', + + // square + ' if (shapeVar == 3.0) {', + ' float side = sqrt(2.0)/2.0;', + ' dist = distToLine(vec2(-side, -side), vec2(side, -side), unitVar);', + ' dist = min(dist, distToLine(vec2(side, -side), vec2(side, side), unitVar));', + ' dist = min(dist, distToLine(vec2(side, side), vec2(-side, side), unitVar));', + ' dist = min(dist, distToLine(vec2(-side, side), vec2(-side, -side), unitVar));', + ' dist = 1.0 - dist;', + ' }', + + // star + ' if (shapeVar == 4.0) {', + ' float h = 1.0;', + ' float x = h * tan(PI/10.0);', + ' vec2 p1 = vec2(0.0, -h);', + ' vec2 p2 = vec2(x, 0);', + ' vec2 p3 = vec2(-x, 0);', + ' float angle = 0.0;', + ' dist = 1.0 - distToAngle(rotate(p1, angle), rotate(p2, angle), rotate(p3, angle), unitVar);', + ' angle = angle + 2.0*PI/5.0;', + ' dist = min(dist, 1.0 - distToAngle(rotate(p1, angle), rotate(p2, angle), rotate(p3, angle), unitVar));', + ' angle = angle + 2.0*PI/5.0;', + ' dist = min(dist, 1.0 - distToAngle(rotate(p1, angle), rotate(p2, angle), rotate(p3, angle), unitVar));', + ' angle = angle + 2.0*PI/5.0;', + ' dist = min(dist, 1.0 - distToAngle(rotate(p1, angle), rotate(p2, angle), rotate(p3, angle), unitVar));', + ' angle = angle + 2.0*PI/5.0;', + ' dist = min(dist, 1.0 - distToAngle(rotate(p1, angle), rotate(p2, angle), rotate(p3, angle), unitVar));', + ' }', + + // triangle-up + ' if (shapeVar == 5.0) {', + ' vec2 p1 = vec2(0.0, -sqrt(2.0)/2.0);', + ' vec2 p2 = vec2(sqrt(6.0)/3.0, sqrt(2.0)/2.0);', + ' vec2 p3 = vec2(-sqrt(6.0)/3.0, sqrt(2.0)/2.0);', + ' dist = distToLine(p1, p2, unitVar);', + ' dist = min(dist, distToLine(p2, p3, unitVar));', + ' dist = min(dist, distToLine(p3, p1, unitVar));', + ' dist = 1.0 - dist;', + ' }', + + // triangle-right + ' if (shapeVar == 6.0) {', + ' vec2 p1 = vec2(sqrt(2.0)/2.0, 0.0);', + ' vec2 p2 = vec2(-sqrt(2.0)/2.0, sqrt(6.0)/3.0);', + ' vec2 p3 = vec2(-sqrt(2.0)/2.0, -sqrt(6.0)/3.0);', + ' dist = distToLine(p1, p2, unitVar);', + ' dist = min(dist, distToLine(p2, p3, unitVar));', + ' dist = min(dist, distToLine(p3, p1, unitVar));', + ' dist = 1.0 - dist;', + ' }', + + // triangle-down + ' if (shapeVar == 7.0) {', + ' vec2 p1 = vec2(0.0, sqrt(2.0)/2.0);', + ' vec2 p2 = vec2(-sqrt(6.0)/3.0, -sqrt(2.0)/2.0);', + ' vec2 p3 = vec2(sqrt(6.0)/3.0, -sqrt(2.0)/2.0);', + ' dist = distToLine(p1, p2, unitVar);', + ' dist = min(dist, distToLine(p2, p3, unitVar));', + ' dist = min(dist, distToLine(p3, p1, unitVar));', + ' dist = 1.0 - dist;', + ' }', + + // triangle-left + ' if (shapeVar == 8.0) {', + ' vec2 p1 = vec2(-sqrt(2.0)/2.0, 0.0);', + ' vec2 p2 = vec2(sqrt(2.0)/2.0, -sqrt(6.0)/3.0);', + ' vec2 p3 = vec2(sqrt(2.0)/2.0, sqrt(6.0)/3.0);', + ' dist = distToLine(p1, p2, unitVar);', + ' dist = min(dist, distToLine(p2, p3, unitVar));', + ' dist = min(dist, distToLine(p3, p1, unitVar));', + ' dist = 1.0 - dist;', + ' }', + + // wye + ' if (shapeVar == 9.0) {', + ' float h = 12.0 / (12.0 + sqrt(12.0));', + ' float y = h / sqrt(12.0) + h;', + ' vec2 p1 = vec2(h / 2.0, y);', + ' vec2 p2 = vec2(-h / 2.0, y);', + ' vec2 p3 = vec2(-h / 2.0, 0.0);', + ' vec2 p4 = vec2(h / 2.0, 0.0);', + ' float angle = 0.0;', + ' dist = 1.0 - distToHull(p1, p2, p3, p4, unitVar);', + ' angle = angle + 2.0*PI/3.0;', + ' dist = min(dist, 1.0 - distToHull(rotate(p1, angle), rotate(p2, angle), rotate(p3, angle), rotate(p4, angle), unitVar));', + ' angle = angle + 2.0*PI/3.0;', + ' dist = min(dist, 1.0 - distToHull(rotate(p1, angle), rotate(p2, angle), rotate(p3, angle), rotate(p4, angle), unitVar));', + ' }', + + ' float endStep = 1.0;', + ' float antialiasDist = 1.0 / sizeVar / 2.0;', + ' float widthDist = strokeWidthVar / sizeVar / 2.0;', + ' vec4 c1;', + ' vec4 c2;', + ' float step;', + ' if (dist < endStep) {', + ' step = smoothstep(endStep - widthDist - antialiasDist, endStep - widthDist + antialiasDist, dist);', + ' if (fillColorVar.a > 0.0) {', + ' c1 = fillColorVar;', + ' } else {', + ' c1 = vec4(strokeColorVar.rgb, 0.0);', + ' }', + ' if (strokeColorVar.a > 0.0) {', + ' c2 = strokeColorVar;', + ' } else {', + ' c2 = vec4(fillColorVar.rgb, 0.0);', + ' }', + ' } else {', + ' step = smoothstep(endStep + widthDist - antialiasDist, endStep + widthDist + antialiasDist, dist);', + ' if (strokeColorVar.a > 0.0) {', + ' c1 = strokeColorVar;', + ' c2 = vec4(strokeColorVar.rgb, 0.0);', + ' } else {', + ' c1 = vec4(fillColorVar.rgb, 0.0);', + ' c2 = vec4(fillColorVar.rgb, 0.0);', + ' }', + ' }', + ' gl_FragColor = mix(c1, c2, step);', + '}' + ].join('\n'); + fragShader = gl.createShader(gl.FRAGMENT_SHADER); + gl.shaderSource(fragShader, fragCode); + gl.compileShader(fragShader); + if (!gl.getShaderParameter(fragShader, gl.COMPILE_STATUS)) { + throw gl.getShaderInfoLog(fragShader); + } + + shaderProgram = gl.createProgram(); + gl.attachShader(shaderProgram, vertShader); + gl.attachShader(shaderProgram, fragShader); + gl.linkProgram(shaderProgram); + + gl._symbolShaderProgram = shaderProgram; + gl._symbolPosLocation = gl.getAttribLocation(shaderProgram, 'pos'); + gl._symbolFillColorLocation = gl.getAttribLocation(shaderProgram, 'fillColor'); + gl._symbolStrokeColorLocation = gl.getAttribLocation(shaderProgram, 'strokeColor'); + gl._symbolFillOpacityLocation = gl.getAttribLocation(shaderProgram, 'fillOpacity'); + gl._symbolStrokeWidthLocation = gl.getAttribLocation(shaderProgram, 'strokeWidth'); + gl._symbolSizeLocation = gl.getAttribLocation(shaderProgram, 'size'); + gl._symbolShapeLocation = gl.getAttribLocation(shaderProgram, 'shape'); + gl._symbolStrokeOpacityLocation = gl.getAttribLocation(shaderProgram, 'strokeOpacity'); + gl._symbolUnitLocation = gl.getAttribLocation(shaderProgram, 'unit'); + gl._symbolMatrixLocation = gl.getUniformLocation(shaderProgram, 'matrix'); + gl._symbolClipLocation = gl.getUniformLocation(shaderProgram, 'clip'); + + // rect shader + + vertCode = [ + 'attribute vec3 pos;', + 'attribute vec3 fillColor;', + 'attribute vec3 strokeColor;', + 'attribute float fillOpacity;', + 'attribute float strokeWidth;', + 'attribute float strokeOpacity;', + 'attribute float cornerRadius;', + 'attribute vec2 size;', + 'attribute vec2 unit;', + 'uniform mat4 matrix;', + 'varying vec4 fillColorVar;', + 'varying vec4 strokeColorVar;', + 'varying float strokeWidthVar;', + 'varying float cornerRadiusVar;', + 'varying float factorVar;', + 'varying vec2 sizeVar;', + 'varying vec2 unitVar;', + 'varying vec4 positionVar;', + 'void main(void)', + '{', + ' strokeWidthVar = strokeWidth;', + ' fillColorVar = vec4(fillColor, fillOpacity);', + ' strokeColorVar = vec4(strokeColor, strokeOpacity);', + ' cornerRadiusVar = cornerRadius;', + ' sizeVar = size;', + ' unitVar = unit;', + ' factorVar = max(size.x, size.y) + strokeWidth + 1.0;', + ' positionVar = vec4(pos.xy + factorVar*unitVar - 0.5*strokeWidth - 0.5, -1.0, 1.0);', + ' gl_Position = matrix * positionVar;', + '}' + ].join('\n'); + vertShader = gl.createShader(gl.VERTEX_SHADER); + gl.shaderSource(vertShader, vertCode); + gl.compileShader(vertShader); + if (!gl.getShaderParameter(vertShader, gl.COMPILE_STATUS)) { + throw gl.getShaderInfoLog(vertShader); + } + + fragCode = [ + 'precision mediump float;', + 'const float PI = 3.1415926535897932384626433832795;', + 'varying vec4 fillColorVar;', + 'varying vec4 strokeColorVar;', + 'varying float strokeWidthVar;', + 'varying float cornerRadiusVar;', + 'varying float factorVar;', + 'varying vec2 unitVar;', + 'varying vec2 sizeVar;', + 'varying vec4 positionVar;', + 'uniform vec4 clip;', + + 'float distToLine(vec2 pt1, vec2 pt2, vec2 testPt)', + '{', + ' vec2 lineDir = pt2 - pt1;', + ' vec2 perpDir = vec2(lineDir.y, -lineDir.x);', + ' vec2 dirToPt1 = pt1 - testPt;', + ' return dot(normalize(perpDir), dirToPt1);', + '}', + + 'float cornerDist(vec2 pt, float radius, float xdir, float ydir, vec2 testPt)', + '{', + ' if (xdir * (pt.x - testPt.x) > 0.0) return 1.0;', + ' if (ydir * (pt.y - testPt.y) > 0.0) return 1.0;', + ' return radius - length(pt - testPt);', + '}', + + 'void main () {', + ' if (positionVar.x < clip[0] || positionVar.x > clip[2] || positionVar.y < clip[1] || positionVar.y > clip[3]) {', + ' discard;', + ' }', + ' float delta = (0.5 + 0.5*strokeWidthVar)/factorVar;', + ' float xmax = (0.5 + 0.5*strokeWidthVar + sizeVar.x)/factorVar;', + ' float ymax = (0.5 + 0.5*strokeWidthVar + sizeVar.y)/factorVar;', + ' vec2 p1 = vec2(delta, delta);', + ' vec2 p2 = vec2(xmax, delta);', + ' vec2 p3 = vec2(xmax, ymax);', + ' vec2 p4 = vec2(delta, ymax);', + ' float dist = distToLine(p1, p2, unitVar);', + ' dist = min(dist, distToLine(p2, p3, unitVar));', + ' dist = min(dist, distToLine(p3, p4, unitVar));', + ' dist = min(dist, distToLine(p4, p1, unitVar));', + + ' if (cornerRadiusVar > 0.0) {', + ' delta = (0.5 + 0.5*strokeWidthVar + cornerRadiusVar)/factorVar;', + ' xmax = (0.5 + 0.5*strokeWidthVar + sizeVar.x - cornerRadiusVar)/factorVar;', + ' ymax = (0.5 + 0.5*strokeWidthVar + sizeVar.y - cornerRadiusVar)/factorVar;', + ' p1 = vec2(delta, delta);', + ' p2 = vec2(xmax, delta);', + ' p3 = vec2(xmax, ymax);', + ' p4 = vec2(delta, ymax);', + ' dist = min(dist, cornerDist(p1, cornerRadiusVar/factorVar, -1.0, -1.0, unitVar));', + ' dist = min(dist, cornerDist(p2, cornerRadiusVar/factorVar, 1.0, -1.0, unitVar));', + ' dist = min(dist, cornerDist(p3, cornerRadiusVar/factorVar, 1.0, 1.0, unitVar));', + ' dist = min(dist, cornerDist(p4, cornerRadiusVar/factorVar, -1.0, 1.0, unitVar));', + ' }', + + ' dist = 1.0 - dist;', + + ' float endStep = 1.0;', + ' float antialiasDist = 0.5 / factorVar;', + ' float widthDist = 0.5*strokeWidthVar / factorVar;', + ' vec4 c1;', + ' vec4 c2;', + ' float step;', + ' if (dist < endStep) {', + ' step = smoothstep(endStep - widthDist - antialiasDist, endStep - widthDist + antialiasDist, dist);', + ' if (fillColorVar.a > 0.0) {', + ' c1 = fillColorVar;', + ' } else {', + ' c1 = vec4(strokeColorVar.rgb, 0.0);', + ' }', + ' if (strokeWidthVar > 0.0) {', + ' c2 = strokeColorVar;', + ' } else {', + ' c2 = vec4(fillColorVar.rgb, 0.0);', + ' }', + ' } else {', + ' step = smoothstep(endStep + widthDist - antialiasDist, endStep + widthDist + antialiasDist, dist);', + ' if (strokeWidthVar > 0.0) {', + ' c1 = strokeColorVar;', + ' c2 = vec4(strokeColorVar.rgb, 0.0);', + ' } else {', + ' c1 = vec4(fillColorVar.rgb, 0.0);', + ' c2 = vec4(fillColorVar.rgb, 0.0);', + ' }', + ' }', + ' gl_FragColor = mix(c1, c2, step);', + '}' + ].join('\n'); + fragShader = gl.createShader(gl.FRAGMENT_SHADER); + gl.shaderSource(fragShader, fragCode); + gl.compileShader(fragShader); + if (!gl.getShaderParameter(fragShader, gl.COMPILE_STATUS)) { + throw gl.getShaderInfoLog(fragShader); + } + + shaderProgram = gl.createProgram(); + gl.attachShader(shaderProgram, vertShader); + gl.attachShader(shaderProgram, fragShader); + gl.linkProgram(shaderProgram); + + gl._rectShaderProgram = shaderProgram; + gl._rectPosLocation = gl.getAttribLocation(shaderProgram, 'pos'); + gl._rectFillColorLocation = gl.getAttribLocation(shaderProgram, 'fillColor'); + gl._rectStrokeColorLocation = gl.getAttribLocation(shaderProgram, 'strokeColor'); + gl._rectFillOpacityLocation = gl.getAttribLocation(shaderProgram, 'fillOpacity'); + gl._rectStrokeWidthLocation = gl.getAttribLocation(shaderProgram, 'strokeWidth'); + gl._rectSizeLocation = gl.getAttribLocation(shaderProgram, 'size'); + gl._rectStrokeOpacityLocation = gl.getAttribLocation(shaderProgram, 'strokeOpacity'); + gl._rectCornerRadiusLocation = gl.getAttribLocation(shaderProgram, 'cornerRadius'); + gl._rectUnitLocation = gl.getAttribLocation(shaderProgram, 'unit'); + gl._rectMatrixLocation = gl.getUniformLocation(shaderProgram, 'matrix'); + gl._rectClipLocation = gl.getUniformLocation(shaderProgram, 'clip'); + + return canvas; +} diff --git a/packages/vega-scenegraph/package.json b/packages/vega-scenegraph/package.json index 7c0495add..8a965b297 100644 --- a/packages/vega-scenegraph/package.json +++ b/packages/vega-scenegraph/package.json @@ -4,8 +4,8 @@ "description": "Vega scenegraph and renderers.", "license": "BSD-3-Clause", "author": "Jeffrey Heer (http://idl.cs.washington.edu)", - "main": "index.js", - "module": "index.js", + "main": "build/index.js", + "module": "build/index.module.js", "unpkg": "build/vega-scenegraph.min.js", "repository": "vega/vega", "scripts": { diff --git a/yarn.lock b/yarn.lock index 76f94efcd..0d62ea8d2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -298,6 +298,22 @@ jsonpointer "^5.0.0" leven "^3.1.0" +"@babel/cli@^7.19.3": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.21.0.tgz#1868eb70e9824b427fc607610cce8e9e7889e7e1" + integrity sha512-xi7CxyS8XjSyiwUGCfwf+brtJxjW1/ZTcBUkP10xawIEXLX5HzLn+3aXkgxozcP2UhRhtKTmQurw9Uaes7jZrA== + dependencies: + "@jridgewell/trace-mapping" "^0.3.17" + commander "^4.0.1" + convert-source-map "^1.1.0" + fs-readdir-recursive "^1.1.0" + glob "^7.2.0" + make-dir "^2.1.0" + slash "^2.0.0" + optionalDependencies: + "@nicolo-ribaudo/chokidar-2" "2.1.8-no-fsevents.3" + chokidar "^3.4.0" + "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.0", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.8.3": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" @@ -369,7 +385,7 @@ json5 "^2.2.1" semver "^6.3.0" -"@babel/core@^7.15.0": +"@babel/core@^7.15.0", "@babel/core@^7.19.3": version "7.21.4" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.4.tgz#c6dc73242507b8e2a27fd13a9c1814f9fa34a659" integrity sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA== @@ -584,6 +600,13 @@ dependencies: "@babel/types" "^7.18.6" +"@babel/helper-module-imports@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz#ac88b2f76093637489e718a90cec6cf8a9b029af" + integrity sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg== + dependencies: + "@babel/types" "^7.21.4" + "@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.19.0": version "7.19.0" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.19.0.tgz#309b230f04e22c58c6a2c0c0c7e50b216d350c30" @@ -1564,6 +1587,18 @@ babel-plugin-polyfill-regenerator "^0.4.1" semver "^6.3.0" +"@babel/plugin-transform-runtime@^7.19.1": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.21.4.tgz#2e1da21ca597a7d01fc96b699b21d8d2023191aa" + integrity sha512-1J4dhrw1h1PqnNNpzwxQ2UBymJUF8KuPjAAnlLwZcGhHAIqUigFW7cdK6GHoB64ubY4qXQNYknoUeks4Wz7CUA== + dependencies: + "@babel/helper-module-imports" "^7.21.4" + "@babel/helper-plugin-utils" "^7.20.2" + babel-plugin-polyfill-corejs2 "^0.3.3" + babel-plugin-polyfill-corejs3 "^0.6.0" + babel-plugin-polyfill-regenerator "^0.4.1" + semver "^6.3.0" + "@babel/plugin-transform-shorthand-properties@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz#6d6df7983d67b195289be24909e3f12a8f664dc9" @@ -1723,7 +1758,7 @@ core-js-compat "^3.25.1" semver "^6.3.0" -"@babel/preset-env@^7.15.0": +"@babel/preset-env@^7.15.0", "@babel/preset-env@^7.19.3": version "7.21.4" resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.21.4.tgz#a952482e634a8dd8271a3fe5459a16eb10739c58" integrity sha512-2W57zHs2yDLm6GD5ZpvNn71lZ0B/iypSdIeq25OurDKji6AdzV07qp4s3n1/x5BqtiGaTrPN3nerlSCaC5qNTw== @@ -1860,6 +1895,14 @@ core-js-pure "^3.25.1" regenerator-runtime "^0.13.4" +"@babel/runtime-corejs3@^7.19.4": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.21.0.tgz#6e4939d9d9789ff63e2dc58e88f13a3913a24eba" + integrity sha512-TDD4UJzos3JJtM+tHX+w2Uc+KWj7GV+VKKFdMVd2Rx8sdA19hcc3P3AHFYd5LVOw+pYuSd5lICC3gm52B6Rwxw== + dependencies: + core-js-pure "^3.25.1" + regenerator-runtime "^0.13.11" + "@babel/runtime@^7.0.0": version "7.20.1" resolved "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.20.1.tgz#1148bb33ab252b165a06698fde7576092a78b4a9" @@ -3902,6 +3945,11 @@ resolved "https://registry.yarnpkg.com/@microsoft/load-themed-styles/-/load-themed-styles-1.10.295.tgz#d3c8d7ab186f422727ba112d6ebe5fe8e41051d9" integrity sha512-W+IzEBw8a6LOOfRJM02dTT7BDZijxm+Z7lhtOAz1+y9vQm1Kdz9jlAO+qCEKsfxtUOmKilW8DIRqFw2aUgKeGg== +"@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3": + version "2.1.8-no-fsevents.3" + resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz#323d72dd25103d0c4fbdce89dadf574a787b1f9b" + integrity sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ== + "@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1": version "5.1.1-v1" resolved "https://registry.npmmirror.com/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz#dbf733a965ca47b1973177dc0bb6c889edcfb129" @@ -3993,6 +4041,19 @@ magic-string "^0.25.7" resolve "^1.17.0" +"@rollup/plugin-commonjs@^22.0.2": + version "22.0.2" + resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-22.0.2.tgz#ee8ca8415cda30d383b4096aad5222435b4b69b6" + integrity sha512-//NdP6iIwPbMTcazYsiBMbJW7gfmpHom33u1beiIoHDEM0Q9clvtQB1T0efvMqHeKsGohiHo97BCPCkBXdscwg== + dependencies: + "@rollup/pluginutils" "^3.1.0" + commondir "^1.0.1" + estree-walker "^2.0.1" + glob "^7.1.6" + is-reference "^1.2.1" + magic-string "^0.25.7" + resolve "^1.17.0" + "@rollup/plugin-json@^4.1.0": version "4.1.0" resolved "https://registry.npmmirror.com/@rollup/plugin-json/-/plugin-json-4.1.0.tgz#54e09867ae6963c593844d8bd7a9c718294496f3" @@ -4044,6 +4105,14 @@ "@rollup/pluginutils" "^3.1.0" magic-string "^0.25.7" +"@rollup/plugin-typescript@^11.1.0": + version "11.1.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-typescript/-/plugin-typescript-11.1.0.tgz#4dd2a98475a791200d3e4dd1b8234073ad96c535" + integrity sha512-86flrfE+bSHB69znnTV6kVjkncs2LBMhcTCyxWgRxLyfXfQrxg4UwlAqENnjrrxnSNS/XKCDJCl8EkdFJVHOxw== + dependencies: + "@rollup/pluginutils" "^5.0.1" + resolve "^1.22.1" + "@rollup/pluginutils@^3.0.8", "@rollup/pluginutils@^3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b" @@ -4061,6 +4130,15 @@ estree-walker "^2.0.1" picomatch "^2.2.2" +"@rollup/pluginutils@^5.0.1": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-5.0.2.tgz#012b8f53c71e4f6f9cb317e311df1404f56e7a33" + integrity sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA== + dependencies: + "@types/estree" "^1.0.0" + estree-walker "^2.0.2" + picomatch "^2.3.1" + "@rushstack/eslint-patch@^1.1.0": version "1.2.0" resolved "https://registry.npmmirror.com/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz#8be36a1f66f3265389e90b5f9c9962146758f728" @@ -4434,6 +4512,11 @@ resolved "https://registry.npmmirror.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40" integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ== +"@types/estree@^1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.1.tgz#aa22750962f3bf0e79d753d3cc067f010c95f194" + integrity sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA== + "@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.18": version "4.17.31" resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz#a1139efeab4e7323834bb0226e62ac019f474b2f" @@ -4460,6 +4543,11 @@ dependencies: "@types/node" "*" +"@types/geojson@^7946.0.10": + version "7946.0.10" + resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.10.tgz#6dfbf5ea17142f7f9a043809f1cd4c448cb68249" + integrity sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA== + "@types/graceful-fs@^4.1.2", "@types/graceful-fs@^4.1.3": version "4.1.5" resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" @@ -4656,7 +4744,7 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== -"@types/react-dom@^17.0.1": +"@types/react-dom@^17.0.1", "@types/react-dom@^17.x": version "17.0.19" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.19.tgz#36feef3aa35d045cacd5ed60fe0eef5272f19492" integrity sha512-PiYG40pnQRdPHnlf7tZnp0aQ6q9tspYr72vD61saO6zFCybLfMqwUCN0va1/P+86DXn18ZWeW30Bk7xlC5eEAQ== @@ -4687,7 +4775,7 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@^17", "@types/react@^17.0.2": +"@types/react@*", "@types/react@^17", "@types/react@^17.0.2", "@types/react@^17.x": version "17.0.58" resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.58.tgz#c8bbc82114e5c29001548ebe8ed6c4ba4d3c9fb0" integrity sha512-c1GzVY97P0fGxwGxhYq989j4XwlcHQoto6wQISOC2v6wm3h0PORRWJFHlkRjfGsiG3y1609WdQ+J+tKxvrEd6A== @@ -4886,10 +4974,10 @@ "@typescript-eslint/types" "5.39.0" eslint-visitor-keys "^3.3.0" -"@vercel/analytics@^0.1.8": - version "0.1.8" - resolved "https://registry.npmmirror.com/@vercel/analytics/-/analytics-0.1.8.tgz#71f1f8c7bb98ac0c5c47eb3fb8ccbe8141b9fe47" - integrity sha512-PQrOI8BJ9qUiVJuQfnKiJd15eDjDJH9TBKsNeMrtelT4NAk7d9mBVz1CoZkvoFnHQ0OW7Xnqmr1F2nScfAnznQ== +"@vercel/analytics@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@vercel/analytics/-/analytics-1.0.0.tgz#7041bf3f14d65ab685f053b2fab09c605d12543e" + integrity sha512-RQmj7pv82JwGDHrnKeRc6TtSw2U7rWNubc2IH0ernTzWTj02yr9zvIYiYJeztsBzrJtWv7m8Nz6vxxb+cdEtJw== "@webassemblyjs/ast@1.11.1": version "1.11.1" @@ -5059,6 +5147,11 @@ abortcontroller-polyfill@^1.4.0: resolved "https://registry.yarnpkg.com/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.3.tgz#1b5b487bd6436b5b764fd52a612509702c3144b5" integrity sha512-zetDJxd89y3X99Kvo4qFx8GKlt6GsvN3UcRZHwU6iFA/0KiOmhkTVhe8oRoTBiTVPZu09x3vCra47+w8Yz1+2Q== +abs-svg-path@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/abs-svg-path/-/abs-svg-path-0.1.1.tgz#df601c8e8d2ba10d4a76d625e236a9a39c2723bf" + integrity sha512-d8XPSGjfyzlXC3Xx891DJRyZfqk5JU0BJrDQcsWomFIV1/BIzPW5HDH5iDdWpqWaav0YVIEzT1RHTwWr0FFshA== + accepts@^1.3.5, accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: version "1.3.8" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" @@ -5114,6 +5207,11 @@ acorn@^8.2.4, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.7.1, acorn@^8.8.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== +adaptive-bezier-curve@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/adaptive-bezier-curve/-/adaptive-bezier-curve-1.0.3.tgz#477577abe87d7280d46ca41649f6c22646fe8227" + integrity sha512-mDcwN284LlNAdunqnVmS0PAoDNHKze/PY8zvpCdxzyXD+ZZFeMWQ3FKNBw0VMOd9IfnhIyzAWJDXzRcWnXtoSg== + address@^1.0.1, address@^1.1.2: version "1.2.1" resolved "https://registry.npmmirror.com/address/-/address-1.2.1.tgz#25bb61095b7522d65b357baa11bc05492d4c8acd" @@ -5357,6 +5455,11 @@ array.prototype.reduce@^1.0.4: es-array-method-boxes-properly "^1.0.0" is-string "^1.0.7" +as-number@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/as-number/-/as-number-1.0.0.tgz#acb27e34f8f9d8ab0da9e376f3b8959860f80a66" + integrity sha512-HkI/zLo2AbSRO4fqVkmyf3hms0bJDs3iboHqTrNuwTiCRvdYXM7HFhfhB6Dk51anV2LM/IMB83mtK9mHw4FlAg== + asap@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" @@ -5899,7 +6002,7 @@ check-types@^11.1.1: resolved "https://registry.yarnpkg.com/check-types/-/check-types-11.1.2.tgz#86a7c12bf5539f6324eb0e70ca8896c0e38f3e2f" integrity sha512-tzWzvgePgLORb9/3a0YenggReLKAIb2owL03H2Xdoe5pKcUyWRSEQ8xfCar8t2SIAuEDwtmx2da1YB52YuHQMQ== -chokidar@^3.4.2, chokidar@^3.5.3: +chokidar@^3.4.0, chokidar@^3.4.2, chokidar@^3.5.3: version "3.5.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== @@ -6089,6 +6192,11 @@ commander@7, commander@^7.2.0: resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== +commander@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" + integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== + commander@^8.3.0: version "8.3.0" resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" @@ -6171,6 +6279,11 @@ content-type@^1.0.4, content-type@~1.0.4: resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== +convert-source-map@^1.1.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" + integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== + convert-source-map@^1.4.0, convert-source-map@^1.5.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.8.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" @@ -6546,23 +6659,16 @@ d3-array@3.1.1: dependencies: internmap "1 - 2" -d3-array@^3.2.2: - version "3.2.3" - resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-3.2.3.tgz#39f1f4954e4a09ff69ac597c2d61906b04e84740" - integrity sha512-JRHwbQQ84XuAESWhvIPaUV4/1UYTBOLiOPGWqgFDHZS1D5QN9c57FbH3QpEnQMYiOXNzKUQyGTZf+EVO7RT5TQ== - dependencies: - internmap "1 - 2" +d3-color@*, "d3-color@1 - 3", d3-color@^3.0.1: + version "3.1.0" + resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-3.1.0.tgz#395b2833dfac71507f12ac2f7af23bf819de24e2" + integrity sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA== d3-color@1: version "1.4.1" resolved "https://registry.npmmirror.com/d3-color/-/d3-color-1.4.1.tgz#c52002bf8846ada4424d55d97982fef26eb3bc8a" integrity sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q== -"d3-color@1 - 3", d3-color@^3.0.1: - version "3.1.0" - resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-3.1.0.tgz#395b2833dfac71507f12ac2f7af23bf819de24e2" - integrity sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA== - d3-delaunay@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/d3-delaunay/-/d3-delaunay-6.0.2.tgz#7fd3717ad0eade2fc9939f4260acfb503f984e92" @@ -6683,6 +6789,13 @@ d3-scale@^4.0.2: d3-time "2.1.1 - 3" d3-time-format "2 - 4" +d3-shape@*: + version "3.2.0" + resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-3.2.0.tgz#a1a839cbd9ba45f28674c69d7f855bcf91dfc6a5" + integrity sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA== + dependencies: + d3-path "^3.1.0" + d3-shape@^3.1.0: version "3.1.0" resolved "https://registry.npmmirror.com/d3-shape/-/d3-shape-3.1.0.tgz#c8a495652d83ea6f524e482fca57aa3f8bc32556" @@ -6690,13 +6803,6 @@ d3-shape@^3.1.0: dependencies: d3-path "1 - 3" -d3-shape@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-3.2.0.tgz#a1a839cbd9ba45f28674c69d7f855bcf91dfc6a5" - integrity sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA== - dependencies: - d3-path "^3.1.0" - "d3-time-format@2 - 4", d3-time-format@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-4.1.0.tgz#7ab5257a5041d11ecb4fe70a5c7d16a195bb408a" @@ -6711,13 +6817,6 @@ d3-shape@^3.2.0: dependencies: d3-array "2 - 3" -d3-time@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-3.1.0.tgz#9310db56e992e3c0175e1ef385e545e48a9bb5c7" - integrity sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q== - dependencies: - d3-array "2 - 3" - "d3-timer@1 - 2": version "2.0.0" resolved "https://registry.npmmirror.com/d3-timer/-/d3-timer-2.0.0.tgz#055edb1d170cfe31ab2da8968deee940b56623e6" @@ -7522,7 +7621,7 @@ estree-walker@^1.0.1: resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700" integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg== -estree-walker@^2.0.1: +estree-walker@^2.0.1, estree-walker@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== @@ -7654,6 +7753,15 @@ express@^4.17.3: utils-merge "1.0.1" vary "~1.1.2" +extrude-polyline@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/extrude-polyline/-/extrude-polyline-1.0.6.tgz#7e6afe1f349a4182fa3f61a00d93979b95f18b20" + integrity sha512-fcKIanU/v+tcdgG0+xMbS0C2VZ0/CF3qqxSjHiWfWICh0yFBezPr3SsOhgdzwE5E82plG6p1orEsfSqgldpxVg== + dependencies: + as-number "^1.0.0" + gl-vec2 "^1.0.0" + polyline-miter-util "^1.0.1" + fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3, fast-deep-equal@~3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -7922,6 +8030,11 @@ fs-monkey@^1.0.3: resolved "https://registry.npmmirror.com/fs-monkey/-/fs-monkey-1.0.3.tgz#ae3ac92d53bb328efe0e9a1d9541f6ad8d48e2d3" integrity sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q== +fs-readdir-recursive@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz#e32fc030a2ccee44a6b5371308da54be0b397d27" + integrity sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA== + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -8042,7 +8155,7 @@ glob-to-regexp@^0.4.1: resolved "https://registry.npmmirror.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== -glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: +glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.2.0: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -10244,6 +10357,14 @@ magic-string@^0.25.0, magic-string@^0.25.7: dependencies: sourcemap-codec "^1.4.8" +make-dir@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" + integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== + dependencies: + pify "^4.0.1" + semver "^5.6.0" + make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" @@ -10607,6 +10728,11 @@ normalize-range@^0.1.2: resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA== +normalize-svg-path@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/normalize-svg-path/-/normalize-svg-path-0.1.0.tgz#456360e60ece75fbef7b5d7e160480e7ffd16fe5" + integrity sha512-1/kmYej2iedi5+ROxkRESL/pI02pkg0OBnaR4hJkSIX6+ORzepwbuUXfrdZaPjysTsJInj0Rj5NuX027+dMBvA== + normalize-url@^6.0.1: version "6.1.0" resolved "https://registry.npmmirror.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" @@ -10917,6 +11043,11 @@ parse-json@^5.0.0, parse-json@^5.2.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" +parse-svg-path@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/parse-svg-path/-/parse-svg-path-0.1.2.tgz#7a7ec0d1eb06fa5325c7d3e009b859a09b5d49eb" + integrity sha512-JyPSBnkTJ0AI8GGJLfMXvKq42cj5c006fnLz6fXy6zfoVjJizi8BNTpu8on8ziI1cKy9d9DGNuY17Ce7wuejpQ== + parse5@6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" @@ -11010,6 +11141,11 @@ pify@^3.0.0: resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" integrity sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg== +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== + pirates@^4.0.4: version "4.0.5" resolved "https://registry.npmmirror.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" @@ -12429,7 +12565,7 @@ rollup-plugin-typescript2@^0.34.1: semver "^7.3.7" tslib "^2.4.0" -rollup@^2.43.1, rollup@^2.56.4: +rollup@^2.43.1, rollup@^2.56.4, rollup@^2.79.1: version "2.79.1" resolved "https://registry.npmmirror.com/rollup/-/rollup-2.79.1.tgz#bedee8faef7c9f93a2647ac0108748f497f081c7" integrity sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw== @@ -12733,11 +12869,21 @@ simple-swizzle@^0.2.2: dependencies: is-arrayish "^0.3.1" +simplify-path@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/simplify-path/-/simplify-path-1.1.0.tgz#791b39d25270b0afc035de757ba826e6f1766103" + integrity sha512-qvEyrV36pP6YjRoEAe7ymSqFwurrTQXltcmZaQXFVh8cTWUfHYGeobbMK8V7WHlT9+ysW3GNVkCd6TF8aM0ydw== + sisteransi@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== +slash@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" + integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== + slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" @@ -13221,6 +13367,16 @@ svg-parser@^2.0.2: resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5" integrity sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ== +svg-path-contours@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/svg-path-contours/-/svg-path-contours-2.0.0.tgz#c76a129429db05eb6d7b61e4d1c58d114a8938a2" + integrity sha512-mUpqlUkchMV5lQq2DdPIulNQ2lqPjYTUvY8bUUql/SlOMdPkavijO/oJgvimz31CC4Hvfce6uq+Jn0xzRysmVw== + dependencies: + abs-svg-path "^0.1.1" + adaptive-bezier-curve "^1.0.3" + normalize-svg-path "^0.1.0" + vec2-copy "^1.0.0" + svgo@^1.2.2: version "1.3.2" resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167" @@ -13385,6 +13541,11 @@ terser@^5.0.0, terser@^5.10.0, terser@^5.14.1: commander "^2.20.0" source-map-support "~0.5.20" +tess2@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/tess2/-/tess2-1.0.0.tgz#2e2eb21822294061b83d46cbaefdd09d86ddf593" + integrity sha512-iSWBSOUoPn3cCT26L5Wi6mvVgL11RV4kReSnVIIPdMN7qNpkL5SLKen5BJcWj+ZTN7kK6JrHBdqTV7vvL8g+9w== + test-exclude@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" @@ -13492,6 +13653,14 @@ tree-kill@^1.2.1: resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== +triangulate-contours@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/triangulate-contours/-/triangulate-contours-1.0.2.tgz#c25ddc9f0e0031f3910764cf17f6842d2f8fc274" + integrity sha512-g1p3BRI0iMjkWcpCKgnbBdgiU26kynO9uZmzTotmOilPlch/aGEObyMk0C+OJj1mUZBwPP9/BUkBeYZiUuGKKw== + dependencies: + tess2 "^1.0.0" + xtend "^4.0.0" + tryer@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8" @@ -13516,6 +13685,11 @@ ts-jest@^29.0.3: semver "7.x" yargs-parser "^21.0.1" +ts-lib@^0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/ts-lib/-/ts-lib-0.0.5.tgz#7f319885478de67f575a3b890a3f30fc6159352a" + integrity sha512-pBLcwddLU22ib+vOAzhmTVqQZVN6FD8LtI0Rq4W6BZMOwhOpXqkKRi8f7P5F8KkuPzeunpimLbL0jznWsBeQHg== + ts-loader@^9.4.1: version "9.4.2" resolved "https://registry.npmmirror.com/ts-loader/-/ts-loader-9.4.2.tgz#80a45eee92dd5170b900b3d00abcfa14949aeb78" @@ -13646,6 +13820,11 @@ typescript@^4.5.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== +typescript@^5.0.4: + version "5.0.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.4.tgz#b217fd20119bd61a94d4011274e0ab369058da3b" + integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw== + unbox-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" @@ -13815,16 +13994,16 @@ vary@^1.1.2, vary@~1.1.2: resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== +vec2-copy@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/vec2-copy/-/vec2-copy-1.0.0.tgz#c6eec1d8dad54625194e5f71f8433b62d2f20a7a" + integrity sha512-jeitylCmqqyM4Z2blr4vLpScsROaiJfhN2dFOjn1VK01cM4fi5GNt60L0Zxhm0OT1vYYiv7BKDOZch0YfPA8qw== + vega-canvas@^1.2.5, vega-canvas@^1.2.6: version "1.2.6" resolved "https://registry.yarnpkg.com/vega-canvas/-/vega-canvas-1.2.6.tgz#55e032ce9a62acd17229f6bac66d99db3d6879cd" integrity sha512-rgeYUpslYn/amIfnuv3Sw6n4BGns94OjjZNtUc9IDji6b+K8LGS/kW+Lvay8JX/oFqtulBp8RLcHN6QjqPLA9Q== -vega-canvas@^1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/vega-canvas/-/vega-canvas-1.2.7.tgz#cf62169518f5dcd91d24ad352998c2248f8974fb" - integrity sha512-OkJ9CACVcN9R5Pi9uF6MZBF06pO6qFpDYHWSKBJsdHP5o724KrsgR6UvbnXFH82FdsiTOff/HqjuaG8C7FL+9Q== - vega-crossfilter@~4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/vega-crossfilter/-/vega-crossfilter-4.1.0.tgz#b6c5a728ce987f2514074adb22cf86b9bc63e0c8" @@ -13873,6 +14052,11 @@ vega-event-selector@^3.0.0, vega-event-selector@~3.0.0: resolved "https://registry.yarnpkg.com/vega-event-selector/-/vega-event-selector-3.0.0.tgz#7b855ac0c3ddb59bc5b5caa0d96dbbc9fbd33a4c" integrity sha512-Gls93/+7tEJGE3kUuUnxrBIxtvaNeF01VIFB2Q2Of2hBIBvtHX74jcAdDtkh5UhhoYGD8Q1J30P5cqEBEwtPoQ== +vega-event-selector@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/vega-event-selector/-/vega-event-selector-3.0.1.tgz#b99e92147b338158f8079d81b28b2e7199c2e259" + integrity sha512-K5zd7s5tjr1LiOOkjGpcVls8GsH/f2CWCrWcpKy74gTCp+llCdwz0Enqo013ZlGaRNjfgD/o1caJRt3GSaec4A== + vega-expression@^5.0.0, vega-expression@~5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/vega-expression/-/vega-expression-5.0.0.tgz#938f26689693a1e0d26716030cdaed43ca7abdfb" @@ -13881,6 +14065,14 @@ vega-expression@^5.0.0, vega-expression@~5.0.0: "@types/estree" "^0.0.50" vega-util "^1.16.0" +vega-expression@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/vega-expression/-/vega-expression-5.0.1.tgz#e6a6eff564d2a93496a9bf34cbc78d8942f236a8" + integrity sha512-atfzrMekrcsuyUgZCMklI5ki8cV763aeo1Y6YrfYU7FBwcQEoFhIV/KAJ1vae51aPDGtfzvwbtVIo3WShFCP2Q== + dependencies: + "@types/estree" "^1.0.0" + vega-util "^1.17.1" + vega-force@~4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/vega-force/-/vega-force-4.1.0.tgz#cc8dea972baa52adc60840ff744ebb9e57d8f1f5" @@ -13901,17 +14093,6 @@ vega-format@^1.0.4, vega-format@^1.1.0, vega-format@~1.1.0: vega-time "^2.0.3" vega-util "^1.15.2" -vega-format@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/vega-format/-/vega-format-1.1.1.tgz#92e4876e18064e7ad54f39045f7b24dede0030b8" - integrity sha512-Rll7YgpYbsgaAa54AmtEWrxaJqgOh5fXlvM2wewO4trb9vwM53KBv4Q/uBWCLK3LLGeBXIF6gjDt2LFuJAUtkQ== - dependencies: - d3-array "^3.2.2" - d3-format "^3.1.0" - d3-time-format "^4.1.0" - vega-time "^2.1.1" - vega-util "^1.17.1" - vega-functions@^5.12.1, vega-functions@^5.13.0, vega-functions@~5.13.0: version "5.13.0" resolved "https://registry.yarnpkg.com/vega-functions/-/vega-functions-5.13.0.tgz#c9ab8c6eedbf39f75b424cca6776b1d0b8c74b32" @@ -13994,17 +14175,6 @@ vega-loader@^4.3.2, vega-loader@^4.4.0, vega-loader@~4.5.0: vega-format "^1.1.0" vega-util "^1.16.0" -vega-loader@^4.5.1: - version "4.5.1" - resolved "https://registry.yarnpkg.com/vega-loader/-/vega-loader-4.5.1.tgz#b85262b3cb8376487db0c014a8a13c3a5e6d52ad" - integrity sha512-qy5x32SaT0YkEujQM2yKqvLGV9XWQ2aEDSugBFTdYzu/1u4bxdUSRDREOlrJ9Km3RWIOgFiCkobPmFxo47SKuA== - dependencies: - d3-dsv "^3.0.1" - node-fetch "^2.6.7" - topojson-client "^3.1.0" - vega-format "^1.1.1" - vega-util "^1.17.1" - vega-parser@~6.1.4: version "6.1.4" resolved "https://registry.yarnpkg.com/vega-parser/-/vega-parser-6.1.4.tgz#4868e41af2c9645b6d7daeeb205cfad06b9d465c" @@ -14053,28 +14223,45 @@ vega-scale@^7.0.3, vega-scale@^7.1.1, vega-scale@^7.2.0, vega-scale@~7.2.0: vega-time "^2.1.0" vega-util "^1.17.0" -vega-scale@^7.3.0: - version "7.3.0" - resolved "https://registry.yarnpkg.com/vega-scale/-/vega-scale-7.3.0.tgz#02b83435a892c6d91a87ee7d3d350fac987f464b" - integrity sha512-pMOAI2h+e1z7lsqKG+gMfR6NKN2sTcyjZbdJwntooW0uFHwjLGjMSY7kSd3nSEquF0HQ8qF7zR6gs1eRwlGimw== +vega-scenegraph@^4.10.0: + version "4.10.1-kanaries-patch" dependencies: - d3-array "^3.2.2" - d3-interpolate "^3.0.1" - d3-scale "^4.0.2" - vega-time "^2.1.1" - vega-util "^1.17.1" + d3-path "^3.0.1" + d3-shape "^3.1.0" + vega-canvas "^1.2.5" + vega-loader "^4.4.0" + vega-scale "^7.2.0" + vega-util "^1.15.2" -vega-scenegraph@^4.10.0, vega-scenegraph@^4.9.2, vega-scenegraph@^4.9.3, vega-scenegraph@~4.10.1: - version "4.10.2" - resolved "https://registry.yarnpkg.com/vega-scenegraph/-/vega-scenegraph-4.10.2.tgz#3ae9ad8e99bbf75e2a4f3ebf2c1f9dee7562d245" - integrity sha512-R8m6voDZO5+etwNMcXf45afVM3XAtokMqxuDyddRl9l1YqSJfS+3u8hpolJ50c2q6ZN20BQiJwKT1o0bB7vKkA== +vega-scenegraph@^4.9.2: + version "4.10.1-kanaries-patch" dependencies: - d3-path "^3.1.0" - d3-shape "^3.2.0" - vega-canvas "^1.2.7" - vega-loader "^4.5.1" - vega-scale "^7.3.0" - vega-util "^1.17.1" + d3-path "^3.0.1" + d3-shape "^3.1.0" + vega-canvas "^1.2.5" + vega-loader "^4.4.0" + vega-scale "^7.2.0" + vega-util "^1.15.2" + +vega-scenegraph@^4.9.3: + version "4.10.1-kanaries-patch" + dependencies: + d3-path "^3.0.1" + d3-shape "^3.1.0" + vega-canvas "^1.2.5" + vega-loader "^4.4.0" + vega-scale "^7.2.0" + vega-util "^1.15.2" + +vega-scenegraph@~4.10.1: + version "4.10.1-kanaries-patch" + dependencies: + d3-path "^3.0.1" + d3-shape "^3.1.0" + vega-canvas "^1.2.5" + vega-loader "^4.4.0" + vega-scale "^7.2.0" + vega-util "^1.15.2" vega-schema-url-parser@^2.2.0: version "2.2.0" @@ -14111,15 +14298,6 @@ vega-time@^2.0.3, vega-time@^2.1.0, vega-time@~2.1.0: d3-time "^3.0.0" vega-util "^1.15.2" -vega-time@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/vega-time/-/vega-time-2.1.1.tgz#0f1fb4e220dd5ed57401b58fb2293241f049ada0" - integrity sha512-z1qbgyX0Af2kQSGFbApwBbX2meenGvsoX8Nga8uyWN8VIbiySo/xqizz1KrP6NbB6R+x5egKmkjdnyNThPeEWA== - dependencies: - d3-array "^3.2.2" - d3-time "^3.1.0" - vega-util "^1.17.1" - vega-tooltip@^0.28.0: version "0.28.0" resolved "https://registry.yarnpkg.com/vega-tooltip/-/vega-tooltip-0.28.0.tgz#8bae2601ffae5e67622de37108f53f284e9a978b" @@ -14138,6 +14316,16 @@ vega-transforms@~4.10.0: vega-time "^2.1.0" vega-util "^1.16.1" +vega-typings@^0.24.0: + version "0.24.0" + resolved "https://registry.yarnpkg.com/vega-typings/-/vega-typings-0.24.0.tgz#e659286c43c63b68cf29a3131360829d129eeb84" + integrity sha512-FFYf67Dn5VNPbYoYHgO2T9Z1I81qcwrXjwKEe0rlJk0MX7CNWPJr9Y3VZEWfxyEx7J9anAm69hGIv0Ehb2G85A== + dependencies: + "@types/geojson" "^7946.0.10" + vega-event-selector "^3.0.1" + vega-expression "^5.0.1" + vega-util "^1.17.1" + vega-typings@~0.22.0: version "0.22.3" resolved "https://registry.yarnpkg.com/vega-typings/-/vega-typings-0.22.3.tgz#f6c73b5ffcdb152539cfcc5ad240a413af579ba7" @@ -14767,7 +14955,7 @@ xmlchars@^2.2.0: resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== -xtend@^4.0.2: +xtend@^4.0.0, xtend@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==