From cb409c1049b0c860fcb7b0262886c1ec2ac93f55 Mon Sep 17 00:00:00 2001 From: Ben Schmidt Date: Mon, 11 Nov 2024 00:27:15 -0500 Subject: [PATCH] refactor manifests to be lazier --- .gitignore | 1 + dev/Everyone.svelte | 63 ++++ dev/FourClasses.svelte | 4 +- dev/Main.svelte | 2 + integers.html | 2 - package-lock.json | 663 ++++++++++++++++++++++++++++++++++++++++- package.json | 3 +- src/Deeptable.ts | 138 ++------- src/interaction.ts | 7 +- src/regl_rendering.ts | 22 +- src/rendering.ts | 13 +- src/scatterplot.ts | 4 +- src/selection.ts | 4 +- src/tests/tix.spec.ts | 18 ++ src/tile.ts | 241 +++++++-------- src/tixrixqid.ts | 24 +- src/types.ts | 17 +- src/typing.ts | 13 +- tsconfig.json | 2 +- uv.lock | 4 +- 20 files changed, 931 insertions(+), 314 deletions(-) create mode 100644 dev/Everyone.svelte create mode 100644 src/tests/tix.spec.ts diff --git a/.gitignore b/.gitignore index 1aedca8b7..31bf4d2dd 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,4 @@ test_pages cities.html assets tmp.csv +newtiles diff --git a/dev/Everyone.svelte b/dev/Everyone.svelte new file mode 100644 index 000000000..94beabdca --- /dev/null +++ b/dev/Everyone.svelte @@ -0,0 +1,63 @@ + + +
+ +
+ +
+ + diff --git a/dev/FourClasses.svelte b/dev/FourClasses.svelte index c0dff9007..881bede67 100644 --- a/dev/FourClasses.svelte +++ b/dev/FourClasses.svelte @@ -8,8 +8,8 @@ import SelectPoints from './svelte/SelectPoints.svelte'; const startSize = 2; const prefs = { - source_url: '/tiles', - max_points: 1000000, + source_url: '/newtiles', + max_points: 10, alpha: 35, // Target saturation for the full page. zoom_balance: 0.22, // Rate at which points increase size. https://observablehq.com/@bmschmidt/zoom-strategies-for-huge-scatterplots-with-three-js point_size: startSize, // Default point size before application of size scaling diff --git a/dev/Main.svelte b/dev/Main.svelte index 6228840c6..ba7cf77c9 100644 --- a/dev/Main.svelte +++ b/dev/Main.svelte @@ -7,12 +7,14 @@ import FourClasses from './FourClasses.svelte'; import SinglePoint from './SinglePoint.svelte'; + import Everyone from './Everyone.svelte'; import LabelMaker from './submodules/LabelMaker.svelte'; const modes = { FourClasses, SinglePoint, LabelMaker, + Everyone, }; $: mode = ''; diff --git a/integers.html b/integers.html index 7d33645fe..112971e76 100644 --- a/integers.html +++ b/integers.html @@ -90,7 +90,6 @@ const batch = make_batch(i * SIZE, SIZE); batches.push(batch); window.b = batch; - console.log(i); } const table = new Table([batches]); return table; @@ -175,7 +174,6 @@ op: 'gt', a: 0, }; - console.log(JSON.stringify(encoding, null, 2)); plot.plotAPI({ encoding, }); diff --git a/package-lock.json b/package-lock.json index e78d519fd..4e9f09dbf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -53,7 +53,8 @@ "typedoc": "^0.26.6", "typescript": "^5.5.4", "uvu": "^0.5.6", - "vite": "^5.4.3" + "vite": "^5.4.3", + "vitest": "^2.1.4" }, "peerDependencies": { "apache-arrow": ">=11.0.0" @@ -1548,6 +1549,129 @@ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", "dev": true }, + "node_modules/@vitest/expect": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.4.tgz", + "integrity": "sha512-DOETT0Oh1avie/D/o2sgMHGrzYUFFo3zqESB2Hn70z6QB1HrS2IQ9z5DfyTqU8sg4Bpu13zZe9V4+UTNQlUeQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "2.1.4", + "@vitest/utils": "2.1.4", + "chai": "^5.1.2", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.4.tgz", + "integrity": "sha512-Ky/O1Lc0QBbutJdW0rqLeFNbuLEyS+mIPiNdlVlp2/yhJ0SbyYqObS5IHdhferJud8MbbwMnexg4jordE5cCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "2.1.4", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.12" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/mocker/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/@vitest/pretty-format": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.4.tgz", + "integrity": "sha512-L95zIAkEuTDbUX1IsjRl+vyBSLh3PwLLgKpghl37aCK9Jvw0iP+wKwIFhfjdUtA2myLgjrG6VU6JCFLv8q/3Ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.4.tgz", + "integrity": "sha512-sKRautINI9XICAMl2bjxQM8VfCMTB0EbsBc/EDFA57V6UQevEKY/TOPOF5nzcvCALltiLfXWbq4MaAwWx/YxIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "2.1.4", + "pathe": "^1.1.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.4.tgz", + "integrity": "sha512-3Kab14fn/5QZRog5BPj6Rs8dc4B+mim27XaKWFWHWA87R56AKjHTGcBFKpvZKDzC4u5Wd0w/qKsUIio3KzWW4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "2.1.4", + "magic-string": "^0.30.12", + "pathe": "^1.1.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.4.tgz", + "integrity": "sha512-4JOxa+UAizJgpZfaCPKK2smq9d8mmjZVPMt2kOsg/R8QkoRzydHH1qHxIYNvr1zlEaFj4SXiaaJWxq/LPLKaLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^3.0.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.4.tgz", + "integrity": "sha512-MXDnZn0Awl2S86PSNIim5PWXgIAx8CIkzu35mBdSApUip6RFOGXBCf3YFyeEu8n1IHk4bWD46DeYFu9mQlFIRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "2.1.4", + "loupe": "^3.1.2", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/acorn": { "version": "7.4.1", "dev": true, @@ -1778,6 +1902,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -1912,6 +2046,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/call-bind": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", @@ -1982,6 +2126,23 @@ } ] }, + "node_modules/chai": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.2.tgz", + "integrity": "sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/chalk": { "version": "2.4.2", "dev": true, @@ -2080,6 +2241,16 @@ "node": ">=8" } }, + "node_modules/check-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, "node_modules/ci-info": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.0.0.tgz", @@ -2487,11 +2658,13 @@ } }, "node_modules/debug": { - "version": "4.3.4", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dev": true, "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -2511,6 +2684,16 @@ "node": ">=0.10.0" } }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/deep-is": { "version": "0.1.3", "dev": true, @@ -3391,6 +3574,16 @@ "node": ">=0.8.x" } }, + "node_modules/expect-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz", + "integrity": "sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/falafel": { "version": "2.2.4", "dev": true, @@ -4665,6 +4858,13 @@ "node": ">=0.10.0" } }, + "node_modules/loupe": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.2.tgz", + "integrity": "sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==", + "dev": true, + "license": "MIT" + }, "node_modules/lunr": { "version": "2.3.9", "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", @@ -4672,10 +4872,11 @@ "dev": true }, "node_modules/magic-string": { - "version": "0.30.11", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", - "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", + "version": "0.30.12", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz", + "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } @@ -4878,7 +5079,9 @@ } }, "node_modules/ms": { - "version": "2.1.2", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, "license": "MIT" }, @@ -5160,6 +5363,23 @@ "node": ">=8" } }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", + "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, "node_modules/periscopic": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", @@ -5923,6 +6143,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -6004,6 +6231,13 @@ "node": "*" } }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, "node_modules/static-eval": { "version": "2.1.0", "dev": true, @@ -6012,6 +6246,13 @@ "escodegen": "^1.11.1" } }, + "node_modules/std-env": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz", + "integrity": "sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==", + "dev": true, + "license": "MIT" + }, "node_modules/stream-read-all": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/stream-read-all/-/stream-read-all-3.0.1.tgz", @@ -6317,6 +6558,50 @@ "xtend": "~4.0.1" } }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.1.tgz", + "integrity": "sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinypool": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.1.tgz", + "integrity": "sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", + "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", + "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -6693,6 +6978,28 @@ } } }, + "node_modules/vite-node": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.4.tgz", + "integrity": "sha512-kqa9v+oi4HwkG6g8ufRnb5AeplcRw8jUF6/7/Qz1qRQOXHImG8YnLbB+LLszENwFnoBl9xIf9nVdCFzNd7GQEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.7", + "pathe": "^1.1.2", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/vite/node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -6721,6 +7028,72 @@ } } }, + "node_modules/vitest": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.4.tgz", + "integrity": "sha512-eDjxbVAJw1UJJCHr5xr/xM86Zx+YxIEXGAR+bmnEID7z9qWfoxpHw0zdobz+TQAFOLT+nEXz3+gx6nUJ7RgmlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "2.1.4", + "@vitest/mocker": "2.1.4", + "@vitest/pretty-format": "^2.1.4", + "@vitest/runner": "2.1.4", + "@vitest/snapshot": "2.1.4", + "@vitest/spy": "2.1.4", + "@vitest/utils": "2.1.4", + "chai": "^5.1.2", + "debug": "^4.3.7", + "expect-type": "^1.1.0", + "magic-string": "^0.30.12", + "pathe": "^1.1.2", + "std-env": "^3.7.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.1", + "tinypool": "^1.0.1", + "tinyrainbow": "^1.2.0", + "vite": "^5.0.0", + "vite-node": "2.1.4", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "2.1.4", + "@vitest/ui": "2.1.4", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, "node_modules/which": { "version": "2.0.2", "dev": true, @@ -6770,6 +7143,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/window-size": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", @@ -7807,6 +8197,90 @@ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", "dev": true }, + "@vitest/expect": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.4.tgz", + "integrity": "sha512-DOETT0Oh1avie/D/o2sgMHGrzYUFFo3zqESB2Hn70z6QB1HrS2IQ9z5DfyTqU8sg4Bpu13zZe9V4+UTNQlUeQA==", + "dev": true, + "requires": { + "@vitest/spy": "2.1.4", + "@vitest/utils": "2.1.4", + "chai": "^5.1.2", + "tinyrainbow": "^1.2.0" + } + }, + "@vitest/mocker": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.4.tgz", + "integrity": "sha512-Ky/O1Lc0QBbutJdW0rqLeFNbuLEyS+mIPiNdlVlp2/yhJ0SbyYqObS5IHdhferJud8MbbwMnexg4jordE5cCoQ==", + "dev": true, + "requires": { + "@vitest/spy": "2.1.4", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.12" + }, + "dependencies": { + "estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "requires": { + "@types/estree": "^1.0.0" + } + } + } + }, + "@vitest/pretty-format": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.4.tgz", + "integrity": "sha512-L95zIAkEuTDbUX1IsjRl+vyBSLh3PwLLgKpghl37aCK9Jvw0iP+wKwIFhfjdUtA2myLgjrG6VU6JCFLv8q/3Ww==", + "dev": true, + "requires": { + "tinyrainbow": "^1.2.0" + } + }, + "@vitest/runner": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.4.tgz", + "integrity": "sha512-sKRautINI9XICAMl2bjxQM8VfCMTB0EbsBc/EDFA57V6UQevEKY/TOPOF5nzcvCALltiLfXWbq4MaAwWx/YxIA==", + "dev": true, + "requires": { + "@vitest/utils": "2.1.4", + "pathe": "^1.1.2" + } + }, + "@vitest/snapshot": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.4.tgz", + "integrity": "sha512-3Kab14fn/5QZRog5BPj6Rs8dc4B+mim27XaKWFWHWA87R56AKjHTGcBFKpvZKDzC4u5Wd0w/qKsUIio3KzWW4Q==", + "dev": true, + "requires": { + "@vitest/pretty-format": "2.1.4", + "magic-string": "^0.30.12", + "pathe": "^1.1.2" + } + }, + "@vitest/spy": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.4.tgz", + "integrity": "sha512-4JOxa+UAizJgpZfaCPKK2smq9d8mmjZVPMt2kOsg/R8QkoRzydHH1qHxIYNvr1zlEaFj4SXiaaJWxq/LPLKaLg==", + "dev": true, + "requires": { + "tinyspy": "^3.0.2" + } + }, + "@vitest/utils": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.4.tgz", + "integrity": "sha512-MXDnZn0Awl2S86PSNIim5PWXgIAx8CIkzu35mBdSApUip6RFOGXBCf3YFyeEu8n1IHk4bWD46DeYFu9mQlFIRg==", + "dev": true, + "requires": { + "@vitest/pretty-format": "2.1.4", + "loupe": "^3.1.2", + "tinyrainbow": "^1.2.0" + } + }, "acorn": { "version": "7.4.1", "dev": true @@ -7967,6 +8441,12 @@ "is-shared-array-buffer": "^1.0.2" } }, + "assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true + }, "available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -8049,6 +8529,12 @@ "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", "dev": true }, + "cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true + }, "call-bind": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", @@ -8090,6 +8576,19 @@ "integrity": "sha512-jRGVy3iSGO5Uutn2owlb5gR6qsGngTw9ZTb4ali9f3glshcNmJ2noam4Mo9zia5P9Dk3jNNydy7vQjuE5dQmfg==", "dev": true }, + "chai": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.2.tgz", + "integrity": "sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==", + "dev": true, + "requires": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + } + }, "chalk": { "version": "2.4.2", "dev": true, @@ -8159,6 +8658,12 @@ } } }, + "check-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", + "dev": true + }, "ci-info": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.0.0.tgz", @@ -8435,10 +8940,12 @@ } }, "debug": { - "version": "4.3.4", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dev": true, "requires": { - "ms": "2.1.2" + "ms": "^2.1.3" } }, "decamelize": { @@ -8447,6 +8954,12 @@ "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", "dev": true }, + "deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true + }, "deep-is": { "version": "0.1.3", "dev": true @@ -9079,6 +9592,12 @@ "version": "3.3.0", "dev": true }, + "expect-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz", + "integrity": "sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==", + "dev": true + }, "falafel": { "version": "2.2.4", "dev": true, @@ -10006,6 +10525,12 @@ "signal-exit": "^3.0.0" } }, + "loupe": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.2.tgz", + "integrity": "sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==", + "dev": true + }, "lunr": { "version": "2.3.9", "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", @@ -10013,9 +10538,9 @@ "dev": true }, "magic-string": { - "version": "0.30.11", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", - "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", + "version": "0.30.12", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz", + "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==", "dev": true, "requires": { "@jridgewell/sourcemap-codec": "^1.5.0" @@ -10180,7 +10705,9 @@ "dev": true }, "ms": { - "version": "2.1.2", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "murmurhash-js": { @@ -10369,6 +10896,18 @@ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true }, + "pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true + }, + "pathval": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", + "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", + "dev": true + }, "periscopic": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", @@ -10893,6 +11432,12 @@ "object-inspect": "^1.13.1" } }, + "siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true + }, "signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -10961,6 +11506,12 @@ "version": "0.0.9", "dev": true }, + "stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true + }, "static-eval": { "version": "2.1.0", "dev": true, @@ -10968,6 +11519,12 @@ "escodegen": "^1.11.1" } }, + "std-env": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz", + "integrity": "sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==", + "dev": true + }, "stream-read-all": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/stream-read-all/-/stream-read-all-3.0.1.tgz", @@ -11187,6 +11744,36 @@ "xtend": "~4.0.1" } }, + "tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true + }, + "tinyexec": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.1.tgz", + "integrity": "sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==", + "dev": true + }, + "tinypool": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.1.tgz", + "integrity": "sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==", + "dev": true + }, + "tinyrainbow": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", + "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", + "dev": true + }, + "tinyspy": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", + "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", + "dev": true + }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -11429,6 +12016,18 @@ } } }, + "vite-node": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.4.tgz", + "integrity": "sha512-kqa9v+oi4HwkG6g8ufRnb5AeplcRw8jUF6/7/Qz1qRQOXHImG8YnLbB+LLszENwFnoBl9xIf9nVdCFzNd7GQEg==", + "dev": true, + "requires": { + "cac": "^6.7.14", + "debug": "^4.3.7", + "pathe": "^1.1.2", + "vite": "^5.0.0" + } + }, "vitefu": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-0.2.5.tgz", @@ -11436,6 +12035,34 @@ "dev": true, "requires": {} }, + "vitest": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.4.tgz", + "integrity": "sha512-eDjxbVAJw1UJJCHr5xr/xM86Zx+YxIEXGAR+bmnEID7z9qWfoxpHw0zdobz+TQAFOLT+nEXz3+gx6nUJ7RgmlQ==", + "dev": true, + "requires": { + "@vitest/expect": "2.1.4", + "@vitest/mocker": "2.1.4", + "@vitest/pretty-format": "^2.1.4", + "@vitest/runner": "2.1.4", + "@vitest/snapshot": "2.1.4", + "@vitest/spy": "2.1.4", + "@vitest/utils": "2.1.4", + "chai": "^5.1.2", + "debug": "^4.3.7", + "expect-type": "^1.1.0", + "magic-string": "^0.30.12", + "pathe": "^1.1.2", + "std-env": "^3.7.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.1", + "tinypool": "^1.0.1", + "tinyrainbow": "^1.2.0", + "vite": "^5.0.0", + "vite-node": "2.1.4", + "why-is-node-running": "^2.3.0" + } + }, "which": { "version": "2.0.2", "dev": true, @@ -11469,6 +12096,16 @@ "has-tostringtag": "^1.0.2" } }, + "why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "requires": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + } + }, "window-size": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", diff --git a/package.json b/package.json index aa2d2e3de..435f41e68 100644 --- a/package.json +++ b/package.json @@ -92,6 +92,7 @@ "typedoc": "^0.26.6", "typescript": "^5.5.4", "uvu": "^0.5.6", - "vite": "^5.4.3" + "vite": "^5.4.3", + "vitest": "^2.1.4" } } diff --git a/src/Deeptable.ts b/src/Deeptable.ts index 3c1b6dd7b..2ced0e0ba 100644 --- a/src/Deeptable.ts +++ b/src/Deeptable.ts @@ -1,4 +1,4 @@ -import { Tile, Rectangle, p_in_rect } from './tile'; +import { Tile, Rectangle } from './tile'; import { max, bisectLeft, extent } from 'd3-array'; import type * as DS from './types'; import { @@ -37,6 +37,7 @@ import type { import { DataSelection } from './selection'; import { Some, TupleMap } from './utilityFunctions'; import { getNestedVector } from './regl_rendering'; +import { tileKey_to_tix } from './tixrixqid'; type TransformationStatus = 'queued' | 'in progress' | 'complete' | 'failed'; @@ -96,9 +97,9 @@ export class Deeptable { // The flatTree is a representation of known tiles in the quadtree in a flat format // indexed by tile index (`tix` -- see `tixRixQid`). public flatTree: (Tile | undefined | null)[] = []; + public flatManifest: (StructRowProxy | null | undefined)[] = []; public promise: Promise; public root_tile: Tile; - public manifest?: DS.TileManifest; public fetcherId?: number; private _downloaderId?: number; // Whether the tileset is structured as a pure quadtree. @@ -127,21 +128,21 @@ export class Deeptable { // If no manifest is passed, we still know // that there is a root tile at the root key. - - const defaultManifest: Partial & { key: string } = { - key: rootKey, - children: undefined, - min_ix: 0, - max_ix: Number.MAX_SAFE_INTEGER, - ...(tileManifest || {}), - }; + // const defaultManifest: Partial & { key: string } = { + // key: rootKey, + // children: undefined, + // min_ix: 0, + // max_ix: Number.MAX_SAFE_INTEGER, + // ...(tileManifest || {}), + // }; + this.flatManifest = tileManifest ? flatManifest(tileManifest) : []; // Must come after manifest is set. this.base_url = baseUrl; - this.root_tile = new Tile(defaultManifest, null, this); + this.root_tile = new Tile(rootKey, null, this); // At instantiation, the deeptable isn't ready; only once this // async stuff is done can the deeptable be used. // TODO: Add an async static method as the preferred initialization method. - this.promise = this._makePromise(defaultManifest); + this.promise = this._makePromise(); } /** @@ -160,7 +161,7 @@ export class Deeptable { baseUrl: string; plot?: Scatterplot | null; }) { - let manifest: DS.TileManifest; + let manifest: Table; if (tileProxy !== undefined) { throw new Error('Not yet supported'); } else { @@ -181,15 +182,14 @@ export class Deeptable { /** * Internal function to ensure */ - protected _makePromise( - tileManifest: Partial & { key: string }, - ): Promise { + protected _makePromise(): Promise { + const { flatManifest } = this; return this.root_tile.preprocessRootTileInfo().then(async () => { const batch = await this.root_tile.get_arrow(null); const schema = batch.schema; // TODO: Cleaner check that it's a lazy manifest - if (!tileManifest.max_ix) { - this.root_tile.manifest = + if (!flatManifest[0]) { + this.root_tile.metadata = await this.root_tile.deriveManifestInfoFromTileMetadata(); } if (schema.metadata.has('sidecars')) { @@ -242,7 +242,7 @@ export class Deeptable { if (!this.root_tile.hasLoadedColumn('x')) { throw new Error("Can't access extent without a root tile"); } - return this.root_tile.manifest.extent; + return this.root_tile.metadata.extent; } /** @@ -462,27 +462,6 @@ export class Deeptable { return this.extents.get(key); } - *points(bbox: Rectangle | undefined, max_ix = 1e99) { - const stack: Tile[] = [this.root_tile]; - // undefined just for the check in the iterator. - let current: Tile | undefined; - while ((current = stack.shift())) { - if ( - current.hasLoadedColumn('ix') && - (bbox === undefined || current.is_visible(max_ix, bbox)) - ) { - for (const point of current) { - if ( - p_in_rect([point.x as number, point.y as number], bbox) && - point.ix <= max_ix - ) { - yield point; - } - } - stack.push(...current.loadedChildren); - } - } - } /** * Map a function against all tiles. * It is often useful simply to invoke Deeptable.map(d => d) to @@ -518,7 +497,6 @@ export class Deeptable { filter: (t: Tile) => boolean = () => true, ) { // Visit all children with a callback function. - const stack: Tile[] = [this.root_tile]; const after_stack = []; let current: Tile | undefined; @@ -531,6 +509,7 @@ export class Deeptable { if (!filter(current)) { continue; } + // console.log(current.key, current.loadedChildren); // Only create children for downloaded tiles. stack.push(...current.loadedChildren); } @@ -1212,76 +1191,19 @@ function minTwo(a: number, b: number) { return a > b ? b : a; } -type RowFormatManifest = { - key: string; - nPoints: number; - min_ix: number; - max_ix: number; - extent: string; // JSON deserializes to a rectangle. -}; - -export type TileManifest = { - key: string; - // The number of data points in that specific tile. - nPoints: number; - children: TileManifest[]; - min_ix: number; - max_ix: number; - extent: Rectangle; -}; - -export async function loadTileManifest(url: string): Promise { +export async function loadTileManifest(url: string): Promise { const data = await fetch(url).then((d) => d.arrayBuffer()); const tb = tableFromIPC(data); - const rows: RowFormatManifest[] = [...tb].map( - ({ key, nPoints, min_ix, max_ix, extent }: RowFormatManifest) => { - return { - key, // string - nPoints: Number(nPoints), // Number because this can come in as a BigInt - min_ix: Number(min_ix), // BigInt -> Number cast - max_ix: Number(max_ix), // BigInt -> Number cast. - extent, // Leave as unparsed json. - } as const; - }, - ); - return parseManifest(rows); + return tb; } -/** - * We receive a manifest as a list of tiles, but deepscatter wants to receive a nested tree. - * This function converts from one to the other, using the format of the quadtree to check - * which of all possible children exist. - * - * @param raw The manifest as it comes in from the - * @returns A tree-structured manifest suitable for passing to deeptable's "tileManifest" argument. - */ -export function parseManifest(rows: RowFormatManifest[]): TileManifest { - const lookup = Object.fromEntries(rows.map((d) => [d.key, d])); - - /** - * Given a row, finds its children and recursively - * @param input The row to insert. - * @returns A tree-shaped manifest. - */ - function buildNetwork(input: RowFormatManifest): TileManifest { - const children: TileManifest[] = []; - const [z, x, y] = input.key.split('/').map((i) => parseInt(i)); - for (const i of [0, 1]) { - for (const j of [0, 1]) { - const childKey = `${z + 1}/${x * 2 + i}/${y * 2 + j}`; - if (lookup[childKey]) { - const manifest = buildNetwork(lookup[childKey]); - children.push(manifest); - } - } - } - return { - ...input, - extent: JSON.parse(input.extent) as Rectangle, - children, - }; +export function flatManifest( + table: Table>, +): (StructRowProxy | undefined | null)[] { + const flatTree: (StructRowProxy | undefined | null)[] = []; + for (const row of [...table]) { + const tix = tileKey_to_tix((row as StructRowProxy)['key'] as string); + flatTree[tix] = row as StructRowProxy; } - const networked = buildNetwork(lookup['0/0/0']); - - return networked; + return flatTree; } diff --git a/src/interaction.ts b/src/interaction.ts index 22e256229..a2231f4a5 100644 --- a/src/interaction.ts +++ b/src/interaction.ts @@ -1,7 +1,7 @@ /* eslint-disable no-underscore-dangle */ /* eslint-disable @typescript-eslint/unbound-method */ -import { select } from 'd3-selection'; +import { BaseType, select } from 'd3-selection'; import { timer } from 'd3-timer'; import { D3ZoomEvent, zoom, zoomIdentity } from 'd3-zoom'; import { mean } from 'd3-array'; @@ -39,8 +39,8 @@ export class Zoom { public prefs: DS.APICall; public svg_element_selection: d3.Selection< d3.ContainerElement, - Record, - any, + Record, + HTMLElement, any >; public width: number; @@ -131,6 +131,7 @@ export class Zoom { zoom_to_bbox(corners: Rectangle, duration = 4000, buffer = 1.111) { // Zooms to two points. const scales = this.scales(); + // eslint-disable-next-line prefer-const let [x0, x1] = corners.x.map(scales.x); const [y0, y1] = corners.y.map(scales.y); diff --git a/src/regl_rendering.ts b/src/regl_rendering.ts index 453854a58..43dfa197e 100644 --- a/src/regl_rendering.ts +++ b/src/regl_rendering.ts @@ -232,15 +232,15 @@ export class ReglRenderer extends Renderer { for (const tile of this.visible_tiles()) { // Do the binding operation; returns truthy if it's already done. if (!this.bufferManager.ready(tile, this.aes.neededFields)) { + // console.log('Not ready for tile', tile.tix); continue; } - const this_props = { number: call_no++, foreground_draw_number: needs_background_pass ? 1 : 1, - tile_id: tile.numeric_id, + tile_id: tile.tix, tile: tile, - count: tile.manifest.nPoints, + count: tile.metadata.nPoints, ...props, } as DS.TileDrawProps; prop_list.push(this_props); @@ -562,7 +562,7 @@ export class ReglRenderer extends Renderer { return null; } for (const tile of this.visible_tiles()) { - if (tile.numeric_id === tile_number) { + if (tile.tix === tile_number) { return tile.record_batch.get(row_number); } } @@ -736,7 +736,9 @@ export class ReglRenderer extends Renderer { u_constant_last_color: () => (this.aes.dim("color").last.constant !== undefined ? this.aes.dim("color").last.constant : [-1, -1, -1]),*/ - u_tile_id: (_: C, props: P) => props.tile_id, + u_tile_id: (_: C, props: P) => { + return props.tile_id; + }, u_width: ({ viewportWidth }: C) => viewportWidth, u_height: ({ viewportHeight }: C) => viewportHeight, u_one_d_aesthetic_map: this.aes.aesthetic_map.one_d_texture, @@ -1049,6 +1051,7 @@ export class BufferManager { */ ready(tile: Tile, needed_dimensions: Iterable): boolean { // We don't allocate buffers for dimensions until they're needed. + for (const keyset of [['ix'], ['ix_in_tile'], ...needed_dimensions] as Some< string[] >) { @@ -1061,14 +1064,13 @@ export class BufferManager { if (keyset[0] === 'ix_in_tile') { this.create_regl_buffer(tile, keyset); } else { - if (tile.readyToUse) { - // tile.get_column(keyset[0]); - } else { - // pass - } return false; } } + } else { + if (tile.key === '0/0/0') { + // console.log('ready'); + } } } return true; diff --git a/src/rendering.ts b/src/rendering.ts index 10a0262c2..cdb53bb2a 100644 --- a/src/rendering.ts +++ b/src/rendering.ts @@ -223,12 +223,12 @@ export class Renderer { if (!this.aes) throw new Error('Aesthetic missing'); const x = this.aes.dim('x') as StatefulAesthetic; - const y = this.aes.dim('x') as StatefulAesthetic; + const y = this.aes.dim('y') as StatefulAesthetic; const natural_display = x.current.field == 'x' && y.current.field == 'y' && - x.last.field == 'x' && - y.last.field == 'y'; + (x.last.field === null || x.last.field == 'x') && + (y.last.field === null || y.last.field == 'y'); const all_tiles = natural_display ? this.scatterplot.deeptable @@ -244,6 +244,13 @@ export class Renderer { .map((d) => d) .filter((tile) => tile.min_ix < this.max_ix); all_tiles.sort((a, b) => a.min_ix - b.min_ix); + for (const tile of all_tiles) { + void tile.allChildren().then((d) => { + for (const child of d) { + if (!child._metadata) void child.populateManifest(); + } + }); + } return all_tiles; } diff --git a/src/scatterplot.ts b/src/scatterplot.ts index 1c92016e1..79d1a437a 100644 --- a/src/scatterplot.ts +++ b/src/scatterplot.ts @@ -373,6 +373,7 @@ export class Scatterplot { tileProxy, }: DS.DataSpec): Promise { if (source_url !== undefined) { + console.log('Loading from source url', source_url); this._root = await Deeptable.fromQuadfeather({ baseUrl: source_url, plot: this, @@ -699,7 +700,7 @@ export class Scatterplot { } // const needed_keys = neededFieldsToPlot(prefs.encoding); - this.deeptable.root_tile.require_columns( + void this.deeptable.root_tile.require_columns( [...needed_keys].map((k) => k[0]), ); // Immediately start loading what we can onto the GPUs, too. @@ -908,7 +909,6 @@ class LabelClick extends SettableFunction { class ClickFunction extends SettableFunction { // eslint-disable-next-line @typescript-eslint/no-unused-vars default(datum: StructRowProxy, plot: Scatterplot | undefined = undefined) { - console.log({ ...datum }); return; } } diff --git a/src/selection.ts b/src/selection.ts index 4a2d3f813..6451fe756 100644 --- a/src/selection.ts +++ b/src/selection.ts @@ -780,7 +780,7 @@ export class DataSelection { }); this.tiles.push(t); this.selectionSize += t.matchCount; - this.evaluationSetSize += tile.manifest.nPoints; + this.evaluationSetSize += tile.metadata.nPoints; // DANGER! Possible race condition. Although the tile loaded // dispatches here, it may take a millisecond or two // before the actual assignment has happened in the recordbatch. @@ -1092,7 +1092,7 @@ export class SortedDataSelection extends DataSelection { }); t.addSort(this.key, this.comparisonGetter); this.selectionSize += t.matchCount; - this.evaluationSetSize += tile.manifest.nPoints; + this.evaluationSetSize += tile.metadata.nPoints; this.tiles.push(t); } diff --git a/src/tests/tix.spec.ts b/src/tests/tix.spec.ts new file mode 100644 index 000000000..0fa09d8b3 --- /dev/null +++ b/src/tests/tix.spec.ts @@ -0,0 +1,18 @@ +import { expect, test } from 'vitest'; +import { tixToChildren, tixToZxy, parentTix } from '../tixrixqid'; + +test('basic tix operations', () => { + expect(tixToZxy(0)).toEqual([0, 0, 0]); + expect(tixToZxy(1)).toEqual([1, 0, 0]); +}); + +test('back and forth', () => { + // Ensure the tix inversion functions work. + for (let i = 0; i < 100; i++) { + const children = tixToChildren(i); + expect(children.length).toBe(4); + for (const child of children) { + expect(parentTix(child)).toEqual(i); + } + } +}); diff --git a/src/tile.ts b/src/tile.ts index f97096434..45486bdbc 100644 --- a/src/tile.ts +++ b/src/tile.ts @@ -22,9 +22,8 @@ export type Rectangle = { // keys?: Array; // } -import type { ArrowBuildable, LazyTileManifest, TileManifest } from './types'; -import { isCompleteManifest } from './typing'; -import { zxyToTix } from './tixrixqid'; +import type { ArrowBuildable, TileMetadata } from './types'; +import { tixToChildren, tixToZxy, zxyToTix } from './tixrixqid'; export type RecordBatchCache = | { @@ -37,9 +36,6 @@ export type RecordBatchCache = ready: true; }; -// Keep a global index of tile numbers. These are used to identify points. -let tile_identifier = 0; - /** * A Tile is a collection of points in the deeptable that are grouped together: * it represents the basic unit of operation for most batched operations in @@ -56,25 +52,25 @@ export class Tile { readonly key: string; // A unique identifier for this tile. protected _batch?: RecordBatch; parent: Tile | null; - private _children: Array = []; + private _children: Array | null = null; private readonly _tix: number; public _highest_known_ix?: number; public deeptable: Deeptable; public _transformations: Record> = {}; - public _deriveManifestFromTileMetadata?: Promise; + public _deriveManifestFromBatchMetadata?: Promise; //private _promiseOfChildren? = Promise; - private _partialManifest: Partial | Partial; - private _manifest?: TileManifest | LazyTileManifest; - - // Does the tile have a loaded manifest and other features sufficient to plot. - public readyToUse = false; + public _metadata: + | null + | (Omit & { extent: Rectangle }); // A cache of fetchCalls for downloaded arrow tables, including any table schema metadata. // Tables may contain more than a single column, so this prevents multiple dispatch. //private _promiseOfChildren: Promise; + /** + * Tile lifecycle: + */ private arrowFetchCalls: Map = new Map(); - public numeric_id: number; //public child_locations: string[] = []; /** @@ -84,24 +80,9 @@ export class Tile { * @param parent The parent tile -- used to navigate through the tree. * @param deeptable The full atlas deeptable of which this tile is a part. */ - constructor( - key: - | string - | (Partial & { key: string }) - | Partial, - parent: Tile | null, - deeptable: Deeptable, - ) { + constructor(key: string, parent: Tile | null, deeptable: Deeptable) { // If it's just initiated with a key, build that into a minimal manifest. - let manifest: - | (Partial & { key: string }) - | Partial; - if (typeof key === 'string') { - manifest = { key }; - } else { - manifest = key; - } - this.key = manifest.key; + this.key = key; const coords = this.key.split('/').map((value) => parseInt(value, 10)); while (coords.length < 3) { coords.push(0); @@ -122,12 +103,27 @@ export class Tile { throw new Error('No deeptable provided'); } - // Grab the next identifier off the queue. This should be async safe with the current setup, but - // the logic might fall apart in truly parallel situations. - this.numeric_id = tile_identifier++; + if (deeptable.flatManifest) { + const row = deeptable.flatManifest[tix]; + const metadata = { + key: this.key, + min_ix: Number(row.min_ix), + max_ix: Number(row.max_ix), + extent: JSON.parse(row.extent as string) as Rectangle, + nPoints: Number(row.nPoints), + children: [] as string[], + }; + for (const child of tixToChildren(tix)) { + if (deeptable.flatManifest[child]) { + metadata.children.push(tixToZxy(child).join('/')); + } + } - if (isCompleteManifest(manifest)) this.manifest = manifest; - this._partialManifest = manifest; + this._metadata = metadata; + this.highest_known_ix = metadata.max_ix; + } else { + this._metadata = null; + } } deleteColumn(colname: string) { @@ -258,66 +254,59 @@ export class Tile { ); } - *points( - bounding: Rectangle | undefined, - sorted = false, - ): Iterable { - //if (!this.is_visible(1e100, bounding)) { - // return; - //} - for (const p of this) { - if (p_in_rect([p.x as number, p.y as number], bounding)) { - yield p; - } - } - if (sorted === false) { - for (const child of this.loadedChildren) { - // TODO: fix - if (!child.record_batch) { - continue; - } - if (bounding && !child.is_visible(1e100, bounding)) { - continue; - } - for (const p of child.points(bounding, sorted)) { - if (p_in_rect([p.x as number, p.y as number], bounding)) { - yield p; - } - } - } - } else { - throw new Error('Sorted iteration not supported'); + get metadata(): Omit & { extent: Rectangle } { + if (!this._metadata) { + throw new Error( + 'Attempted to access manifest on partially loaded tile ' + this.key, + ); } + return this._metadata; } - forEach(callback: (p: StructRowProxy) => void) { - for (const p of this.points(undefined, false)) { - if (p === undefined) { - continue; + private createChildren() { + if (!this._metadata) { + throw new Error('Attempted to create children without metadata.'); + } + const { metadata } = this; + if (!metadata.children) { + // Then the children may be inferred from the manifest. + if (!this.deeptable.flatManifest) { + throw new Error('Attempted to set an incomplete manifest.'); + } + const { tix } = this; + const children: Tile[] = []; + for (const childTix of tixToChildren(tix)) { + if (this.deeptable.flatManifest[childTix]) { + children.push( + new Tile( + this.deeptable.flatManifest[childTix].key, + this, + this.deeptable, + ), + ); + } } - callback(p); + this._children = children; + } else { + this._children = metadata.children.map((k: string) => { + return new Tile(k, this, this.deeptable); + }); } } - get manifest(): TileManifest | LazyTileManifest { - if (!this._manifest) - throw new Error('Attempted to access manifest on partially loaded tile.'); - - return this._manifest; - } - - set manifest(manifest: TileManifest | LazyTileManifest) { + set metadata( + manifest: TileMetadata | (TileMetadata & { extent: Rectangle }), + ) { // Setting the manifest is the thing that spawns children. - if (!manifest.children) { - console.error({ manifest }); - throw new Error('Attempted to set an incomplete manifest.'); - } - this._children = manifest.children.map((k: TileManifest | string) => { - return new Tile(k, this, this.deeptable); - }); + console.log(manifest.max_ix); this.highest_known_ix = manifest.max_ix; - this._manifest = manifest; - this.readyToUse = true; + this._metadata = { + ...manifest, + extent: + typeof manifest.extent === 'string' + ? (JSON.parse(manifest.extent) as Rectangle) + : manifest.extent, + }; } set highest_known_ix(val) { @@ -343,12 +332,12 @@ export class Tile { return this._batch; } - if (this._manifest) { + if (this._metadata) { // This helps support legacy code that may fetch the record_batch // just for its number of rows. return new RecordBatch({ // @ts-expect-error Arrow typing doesn't match this constructor. - __null: vectorFromArray(Array(this.manifest.nPoints)), + __null: vectorFromArray(Array(this.metadata.nPoints)), }); } @@ -358,8 +347,8 @@ export class Tile { } get max_ix(): number { - if (this.manifest?.max_ix !== undefined) { - return this.manifest.max_ix; + if (this.metadata?.max_ix !== undefined) { + return this.metadata.max_ix; } if (this.parent) { return this.parent.max_ix + 1; @@ -368,8 +357,8 @@ export class Tile { } get min_ix() { - if (this._manifest && this.manifest?.min_ix !== undefined) { - return this.manifest.min_ix; + if (this._metadata && this.metadata?.min_ix !== undefined) { + return this.metadata.min_ix; } if (this.parent) { return this.parent.max_ix + 1; @@ -386,8 +375,8 @@ export class Tile { } get extent(): Rectangle { - if (this._manifest && this.manifest?.extent) { - return this.manifest.extent; + if (this._metadata && this.metadata?.extent) { + return this.metadata.extent; } return this.theoretical_extent; } @@ -405,11 +394,8 @@ export class Tile { * Triggers many unnecessary calls: prefer * download instead if possible. */ - if (suffix === null) { - await this.deriveManifestInfoFromTileMetadata(); - } else { - await this.get_arrow(suffix); - } + + await this.get_arrow(suffix); if (this.max_ix < max_ix) { await Promise.all( this.loadedChildren.map( @@ -491,24 +477,11 @@ export class Tile { * @returns void */ async populateManifest(): Promise { - if (this._manifest) { + if (this._metadata) { return; - } else if (this._partialManifest.children) { - if (this._partialManifest.nPoints === undefined) { - console.warn('Something is wrong here.'); - } - this.manifest = { - ...this._partialManifest, - key: this.key, - children: this._partialManifest.children as string[], - min_ix: this.min_ix, - max_ix: this.max_ix, - extent: this.extent, - nPoints: this._partialManifest.nPoints ?? -1, - }; - } else { - this.manifest = await this.deriveManifestInfoFromTileMetadata(); } + console.log('Populating', this.key); + this.metadata = await this.deriveManifestInfoFromTileMetadata(); } preprocessRootTileInfo(): Promise { @@ -536,16 +509,14 @@ export class Tile { }); } - async deriveManifestInfoFromTileMetadata(): Promise< - TileManifest | LazyTileManifest - > { + async deriveManifestInfoFromTileMetadata(): Promise { // This should only be called once per tile. - if (this._deriveManifestFromTileMetadata !== undefined) { - return this._deriveManifestFromTileMetadata; + if (this._deriveManifestFromBatchMetadata !== undefined) { + return this._deriveManifestFromBatchMetadata; } - const manifest: Partial = {}; - this._deriveManifestFromTileMetadata = this.get_arrow(null).then( + const manifest: Partial = {}; + this._deriveManifestFromBatchMetadata = this.get_arrow(null).then( async (batch) => { // For every column in the root tile, // define a transformation for other children that says @@ -569,9 +540,9 @@ export class Tile { const metadata = batch.schema.metadata; const extent = metadata.get('extent'); if (extent) { - manifest.extent = JSON.parse(extent) as Rectangle; + manifest.extent = extent; } else { - manifest.extent = this.theoretical_extent; + manifest.extent = JSON.stringify(this.theoretical_extent); } const children = metadata.get('children'); @@ -610,7 +581,7 @@ export class Tile { }, ); - return this._deriveManifestFromTileMetadata; + return this._deriveManifestFromBatchMetadata; } /** @@ -632,29 +603,17 @@ export class Tile { // The children that have actually been created already. get loadedChildren(): Array { // create or return children. - - // if (this._children === null) { - // throw new Error( - // 'Attempted to access children on a tile before they were determined', - // ); - // } - return this._children; + return this._children || []; } // Asynchronously forces generation of all child // tiles, and then returns them. async allChildren(): Promise> { - if (this._children.length) { - return this._children; - } - if (this._partialManifest?.children) { - for (const child of this.manifest.children) { - const childTile = new Tile(child, this, this.deeptable); - this._children.push(childTile); - } + if (this._children) { return this._children; } await this.populateManifest(); + this.createChildren(); return this._children; } diff --git a/src/tixrixqid.ts b/src/tixrixqid.ts index 402a160fd..6ba3e9f1a 100644 --- a/src/tixrixqid.ts +++ b/src/tixrixqid.ts @@ -32,7 +32,29 @@ export function zxyToTix(z: number, x: number, y: number) { return (4 ** z - 1) / 3 + y * 2 ** z + x; } -function parentTix(tix: number) { +export function tixToChildren(tix: number): [number, number, number, number] { + // I'm sure there's a better way to do this reusing the series + // itself, but this is what + // chat GPT spat out with some refactoring I think it's fast enough. + + const [z, x, y] = tixToZxy(tix); + const zChild = z + 1; + + const children = [ + [zChild, x * 2, y * 2], + [zChild, x * 2 + 1, y * 2], + [zChild, x * 2, y * 2 + 1], + [zChild, x * 2 + 1, y * 2 + 1], + ] as [number, number, number][]; + return children.map((d) => zxyToTix(d[0], d[1], d[2])) as [ + number, + number, + number, + number, + ]; +} + +export function parentTix(tix: number) { const [z, x, y] = tixToZxy(tix); return zxyToTix(z - 1, Math.floor(x / 2), Math.floor(y / 2)); } diff --git a/src/types.ts b/src/types.ts index 6b9b629cc..41d3d6f88 100644 --- a/src/types.ts +++ b/src/types.ts @@ -67,23 +67,14 @@ export type ScatterplotOptions = { // allow certain optimizations. export type TileStructure = 'quadtree' | 'other'; -export type LazyTileManifest = { +export type TileMetadata = { key: string; // The number of data points in that specific tile. nPoints: number; - children: string[]; + children?: string[]; // If children is not passed in metadata, it may be looked up on the tile. min_ix: number; max_ix: number; - extent: Rectangle; -}; -export type TileManifest = { - key: string; - // The number of data points in that specific tile. - nPoints: number; - children: TileManifest[]; - min_ix: number; - max_ix: number; - extent: Rectangle; + extent: string; }; /** @@ -110,7 +101,7 @@ export type DeeptableCreateParams = { // A manifest listing all the tiles in the deeptable. // Currently this must be passed as a recursive structure. - tileManifest?: Partial; + tileManifest?: Table; // A URL for an arrow file manifest. The schema for this manifest // is not yet publically documented: I hope to bundle it into the diff --git a/src/typing.ts b/src/typing.ts index 54d0e80dd..dab9713e0 100644 --- a/src/typing.ts +++ b/src/typing.ts @@ -36,16 +36,9 @@ export function isLabelset(labels: DS.Labelcall): labels is DS.Labelset { // There must be a general function here huh. export function isCompleteManifest( - manifest: Partial | Partial, -): manifest is DS.TileManifest { - for (const k of [ - 'key', - 'nPoints', - 'children', - 'min_ix', - 'max_ix', - 'extent', - ] as const) { + manifest: Partial, +): manifest is DS.TileMetadata { + for (const k of ['key', 'nPoints', 'min_ix', 'max_ix', 'extent'] as const) { if (manifest[k] === undefined) { return false; } diff --git a/tsconfig.json b/tsconfig.json index fdc901916..dd9df7b2f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,7 +5,7 @@ "declarationMap": true, "declaration": true, "module": "esnext", - "moduleResolution": "node", + "moduleResolution": "Bundler", "esModuleInterop": true, "noUnusedLocals": true, "experimentalDecorators": true, diff --git a/uv.lock b/uv.lock index d5044429c..3ff021fd8 100644 --- a/uv.lock +++ b/uv.lock @@ -123,8 +123,8 @@ wheels = [ [[package]] name = "quadfeather" -version = "1.1.2" -source = { git = "https://github.com/bmschmidt/quadfeather#6336d068025fa38b40a0e63ecd525ab716fe8819" } +version = "2.0.0rc1" +source = { git = "https://github.com/bmschmidt/quadfeather#74ead803f6fa5ba9111c706ffa8cba2dedf14076" } dependencies = [ { name = "numpy" }, { name = "pandas" },