diff --git a/compose/neurosynth-frontend/package-lock.json b/compose/neurosynth-frontend/package-lock.json index 9380c6b1..45c86c5a 100644 --- a/compose/neurosynth-frontend/package-lock.json +++ b/compose/neurosynth-frontend/package-lock.json @@ -24,6 +24,7 @@ "@mui/styles": "^5.5.1", "@mui/system": "^5.5.1", "@mui/x-data-grid": "^5.10.0", + "@niivue/niivue": "^0.46.0", "@reactour/tour": "^2.10.3", "@sentry/react": "^7.48.0", "@tanstack/react-table": "^8.20.5", @@ -1493,6 +1494,25 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@lukeed/csprng": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz", + "integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@lukeed/uuid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@lukeed/uuid/-/uuid-2.0.1.tgz", + "integrity": "sha512-qC72D4+CDdjGqJvkFMMEAtancHUQ7/d/tAiHf64z8MopFDmcrtbcJuerDtFceuAfQJ2pDSfCKCtbqoGBNnwg0w==", + "dependencies": { + "@lukeed/csprng": "^1.1.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@mui/base": { "version": "5.0.0-beta.40", "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.40.tgz", @@ -1811,6 +1831,24 @@ "node": ">=6" } }, + "node_modules/@niivue/niivue": { + "version": "0.46.0", + "resolved": "https://registry.npmjs.org/@niivue/niivue/-/niivue-0.46.0.tgz", + "integrity": "sha512-fw4/14lxbb4Q9qEDW+eMMfC2wfGTb0x0cgN1VnHsm1bq2kv/xXBQRQJrloQ1Ma5qNweBwbJynEEPm8fXbN8Bgw==", + "dependencies": { + "@lukeed/uuid": "^2.0.1", + "@ungap/structured-clone": "^1.2.0", + "array-equal": "^1.0.2", + "daikon": "^1.2.46", + "fflate": "^0.8.2", + "gl-matrix": "^3.4.3", + "nifti-reader-js": "^0.6.8", + "rxjs": "^7.8.1" + }, + "optionalDependencies": { + "@rollup/rollup-linux-x64-gnu": "^4.18.1" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -3506,6 +3544,11 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" + }, "node_modules/@vitejs/plugin-react": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.3.tgz", @@ -3640,6 +3683,14 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/@wearemothership/dicom-character-set": { + "version": "1.0.4-opt.1", + "resolved": "https://registry.npmjs.org/@wearemothership/dicom-character-set/-/dicom-character-set-1.0.4-opt.1.tgz", + "integrity": "sha512-stqhnpawYHY2UZKj4RHTF71ab3q3z8S1SO9ToQKjsHQwowUdFVo6YFea93psFux3yqNbRlQjwoCdPjHcD0YQzw==", + "engines": { + "node": ">=10" + } + }, "node_modules/abortcontroller-polyfill": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.5.tgz", @@ -3824,6 +3875,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/array-equal": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.2.tgz", + "integrity": "sha512-gUHx76KtnhEgB3HOuFYiCm3FIdEs6ocM2asHvNTkfu/Y09qQVrrVVaOKENmS2KkSaGoxgXNqC+ZVtR/n0MOkSA==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/asn1": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", @@ -4469,6 +4528,11 @@ "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", "dev": true }, + "node_modules/cssfilter": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz", + "integrity": "sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw==" + }, "node_modules/cssstyle": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.1.0.tgz", @@ -4669,6 +4733,18 @@ "node": ">=12" } }, + "node_modules/daikon": { + "version": "1.2.46", + "resolved": "https://registry.npmjs.org/daikon/-/daikon-1.2.46.tgz", + "integrity": "sha512-S8dTTlsWYTH3LQztjTW9KnNvxDeL2mr2cau0auLdYMJe4TrocYP1PmidHizO3rXUs+gXpBWI1PQ2qvB4b21QFw==", + "dependencies": { + "@wearemothership/dicom-character-set": "^1.0.4-opt.1", + "fflate": "*", + "jpeg-lossless-decoder-js": "2.0.7", + "pako": "^2.1", + "xss": "1.0.14" + } + }, "node_modules/dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -5542,6 +5618,11 @@ "node-fetch": "~2.6.1" } }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==" + }, "node_modules/figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -5797,6 +5878,11 @@ "assert-plus": "^1.0.0" } }, + "node_modules/gl-matrix": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.3.tgz", + "integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==" + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -6747,6 +6833,11 @@ "url": "https://github.com/sponsors/panva" } }, + "node_modules/jpeg-lossless-decoder-js": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/jpeg-lossless-decoder-js/-/jpeg-lossless-decoder-js-2.0.7.tgz", + "integrity": "sha512-tbZlhFkKmx+JaqVMkq47SKWGuXLkIaV8fTbnhO39dYEnQrSShLGuLCGb0n6ntXjtmk6oAWGiIriWOLwj9od0yQ==" + }, "node_modules/js-sha3": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", @@ -7405,6 +7496,14 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/nifti-reader-js": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/nifti-reader-js/-/nifti-reader-js-0.6.8.tgz", + "integrity": "sha512-yIKNVzYFiUcSHazoR+sd6Ka7sUmZTabaVqJRFxbdlAKR1hnPBuNP71g3AyApo37nJ3k41c632QPij5q7gF1YPQ==", + "dependencies": { + "fflate": "*" + } + }, "node_modules/node-fetch": { "version": "2.6.13", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.13.tgz", @@ -7654,6 +7753,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -8377,7 +8481,6 @@ "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dev": true, "dependencies": { "tslib": "^2.1.0" } @@ -9816,6 +9919,26 @@ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "dev": true }, + "node_modules/xss": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.14.tgz", + "integrity": "sha512-og7TEJhXvn1a7kzZGQ7ETjdQVS2UfZyTlsEdDOqvQF7GoxNfY+0YLCzBy1kPdsDDx4QuNAonQPddpsn6Xl/7sw==", + "dependencies": { + "commander": "^2.20.3", + "cssfilter": "0.0.10" + }, + "bin": { + "xss": "bin/xss" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/xss/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", @@ -10833,6 +10956,19 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "@lukeed/csprng": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz", + "integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==" + }, + "@lukeed/uuid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@lukeed/uuid/-/uuid-2.0.1.tgz", + "integrity": "sha512-qC72D4+CDdjGqJvkFMMEAtancHUQ7/d/tAiHf64z8MopFDmcrtbcJuerDtFceuAfQJ2pDSfCKCtbqoGBNnwg0w==", + "requires": { + "@lukeed/csprng": "^1.1.0" + } + }, "@mui/base": { "version": "5.0.0-beta.40", "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.40.tgz", @@ -10975,6 +11111,22 @@ } } }, + "@niivue/niivue": { + "version": "0.46.0", + "resolved": "https://registry.npmjs.org/@niivue/niivue/-/niivue-0.46.0.tgz", + "integrity": "sha512-fw4/14lxbb4Q9qEDW+eMMfC2wfGTb0x0cgN1VnHsm1bq2kv/xXBQRQJrloQ1Ma5qNweBwbJynEEPm8fXbN8Bgw==", + "requires": { + "@lukeed/uuid": "^2.0.1", + "@rollup/rollup-linux-x64-gnu": "^4.18.1", + "@ungap/structured-clone": "^1.2.0", + "array-equal": "^1.0.2", + "daikon": "^1.2.46", + "fflate": "^0.8.2", + "gl-matrix": "^3.4.3", + "nifti-reader-js": "^0.6.8", + "rxjs": "^7.8.1" + } + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -12278,6 +12430,11 @@ "eslint-visitor-keys": "^3.4.3" } }, + "@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" + }, "@vitejs/plugin-react": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.3.tgz", @@ -12372,6 +12529,11 @@ "tinyrainbow": "^1.2.0" } }, + "@wearemothership/dicom-character-set": { + "version": "1.0.4-opt.1", + "resolved": "https://registry.npmjs.org/@wearemothership/dicom-character-set/-/dicom-character-set-1.0.4-opt.1.tgz", + "integrity": "sha512-stqhnpawYHY2UZKj4RHTF71ab3q3z8S1SO9ToQKjsHQwowUdFVo6YFea93psFux3yqNbRlQjwoCdPjHcD0YQzw==" + }, "abortcontroller-polyfill": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.5.tgz", @@ -12497,6 +12659,11 @@ "is-array-buffer": "^3.0.4" } }, + "array-equal": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.2.tgz", + "integrity": "sha512-gUHx76KtnhEgB3HOuFYiCm3FIdEs6ocM2asHvNTkfu/Y09qQVrrVVaOKENmS2KkSaGoxgXNqC+ZVtR/n0MOkSA==" + }, "asn1": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", @@ -12955,6 +13122,11 @@ "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", "dev": true }, + "cssfilter": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz", + "integrity": "sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw==" + }, "cssstyle": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.1.0.tgz", @@ -13108,6 +13280,18 @@ "d3-transition": "2 - 3" } }, + "daikon": { + "version": "1.2.46", + "resolved": "https://registry.npmjs.org/daikon/-/daikon-1.2.46.tgz", + "integrity": "sha512-S8dTTlsWYTH3LQztjTW9KnNvxDeL2mr2cau0auLdYMJe4TrocYP1PmidHizO3rXUs+gXpBWI1PQ2qvB4b21QFw==", + "requires": { + "@wearemothership/dicom-character-set": "^1.0.4-opt.1", + "fflate": "*", + "jpeg-lossless-decoder-js": "2.0.7", + "pako": "^2.1", + "xss": "1.0.14" + } + }, "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -13762,6 +13946,11 @@ "node-fetch": "~2.6.1" } }, + "fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==" + }, "figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -13944,6 +14133,11 @@ "assert-plus": "^1.0.0" } }, + "gl-matrix": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.3.tgz", + "integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==" + }, "glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -14613,6 +14807,11 @@ "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==" }, + "jpeg-lossless-decoder-js": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/jpeg-lossless-decoder-js/-/jpeg-lossless-decoder-js-2.0.7.tgz", + "integrity": "sha512-tbZlhFkKmx+JaqVMkq47SKWGuXLkIaV8fTbnhO39dYEnQrSShLGuLCGb0n6ntXjtmk6oAWGiIriWOLwj9od0yQ==" + }, "js-sha3": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", @@ -15142,6 +15341,14 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "nifti-reader-js": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/nifti-reader-js/-/nifti-reader-js-0.6.8.tgz", + "integrity": "sha512-yIKNVzYFiUcSHazoR+sd6Ka7sUmZTabaVqJRFxbdlAKR1hnPBuNP71g3AyApo37nJ3k41c632QPij5q7gF1YPQ==", + "requires": { + "fflate": "*" + } + }, "node-fetch": { "version": "2.6.13", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.13.tgz", @@ -15303,6 +15510,11 @@ "aggregate-error": "^3.0.0" } }, + "pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==" + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -15812,7 +16024,6 @@ "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dev": true, "requires": { "tslib": "^2.1.0" } @@ -16798,6 +17009,22 @@ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "dev": true }, + "xss": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.14.tgz", + "integrity": "sha512-og7TEJhXvn1a7kzZGQ7ETjdQVS2UfZyTlsEdDOqvQF7GoxNfY+0YLCzBy1kPdsDDx4QuNAonQPddpsn6Xl/7sw==", + "requires": { + "commander": "^2.20.3", + "cssfilter": "0.0.10" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + } + } + }, "yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", diff --git a/compose/neurosynth-frontend/package.json b/compose/neurosynth-frontend/package.json index a11e3719..e371210c 100644 --- a/compose/neurosynth-frontend/package.json +++ b/compose/neurosynth-frontend/package.json @@ -19,6 +19,7 @@ "@mui/styles": "^5.5.1", "@mui/system": "^5.5.1", "@mui/x-data-grid": "^5.10.0", + "@niivue/niivue": "^0.46.0", "@reactour/tour": "^2.10.3", "@sentry/react": "^7.48.0", "@tanstack/react-table": "^8.20.5", diff --git a/compose/neurosynth-frontend/src/components/Visualizer/NiiVueVisualizer.tsx b/compose/neurosynth-frontend/src/components/Visualizer/NiiVueVisualizer.tsx new file mode 100644 index 00000000..e43c5383 --- /dev/null +++ b/compose/neurosynth-frontend/src/components/Visualizer/NiiVueVisualizer.tsx @@ -0,0 +1,104 @@ +import { Box, Slider, Typography } from '@mui/material'; +import { useEffect, useRef, useState } from 'react'; +import { Niivue, SHOW_RENDER } from '@niivue/niivue'; + +let niivue: Niivue; + +const NiiVueVisualizer: React.FC<{ imageURL: string }> = ({ imageURL }) => { + const canvasRef = useRef(null); + const [thresholdPositive, setThresholdPositive] = useState(0); + const [thresholdNegative, setThresholdNegative] = useState(0); + + const handleUpdateThresholdPositive = (event: Event, newValue: number | number[]) => { + if (!niivue) return; + setThresholdPositive(newValue as number); + niivue.volumes[1].cal_min = newValue as number; + niivue.updateGLVolume(); + }; + + const handkleUpdateThresholdNegative = (event: Event, newValue: number | number[]) => { + if (!niivue) return; + setThresholdNegative(newValue as number); + niivue.volumes[1].cal_minNeg = -2; + niivue.volumes[1].cal_maxNeg = newValue as number; + niivue.updateGLVolume(); + }; + + useEffect(() => { + if (!canvasRef.current) return; + + const volumes = [ + { + url: 'https://neurovault.org/static/images/GenericMNI.nii.gz', + colormap: 'gray', + opacity: 1, + }, + { + url: imageURL, + colorMap: 'warm', + colormapNegative: 'winter', + cal_min: 0, + cal_max: 2, + cal_minNeg: -1, + cal_maxNeg: -2, + opacity: 1, + }, + ]; + + niivue = new Niivue({ + show3Dcrosshair: true, + }); + + niivue.opts.isColorbar = true; + niivue.setSliceMM(false); + + niivue.attachToCanvas(canvasRef.current); + niivue.addVolumesFromUrl(volumes).then(() => { + niivue.volumes[1].alphaThreshold = 0; + niivue.volumes[0].colorbarVisible = false; + niivue.volumes[1].cal_minNeg = -1.0; + niivue.volumes[1].cal_maxNeg = -2.0; + niivue.opts.multiplanarShowRender = SHOW_RENDER.ALWAYS; + niivue.setInterpolation(true); + niivue.updateGLVolume(); + }); + }, [imageURL]); + + return ( + + + + + -Threshold + + + + + + +Threshold + + + + + + + + + ); +}; + +export default NiiVueVisualizer; diff --git a/compose/neurosynth-frontend/src/helpers/MetaAnalysis.helpers.tsx b/compose/neurosynth-frontend/src/helpers/MetaAnalysis.helpers.tsx index aff9bc8a..1d8b5182 100644 --- a/compose/neurosynth-frontend/src/helpers/MetaAnalysis.helpers.tsx +++ b/compose/neurosynth-frontend/src/helpers/MetaAnalysis.helpers.tsx @@ -1,8 +1,4 @@ -import { - MetaAnalysisReturn, - NeurovaultFile, - ResultReturn, -} from 'neurosynth-compose-typescript-sdk'; +import { MetaAnalysisReturn, NeurovaultFile, ResultReturn } from 'neurosynth-compose-typescript-sdk'; export const getResultStatus = ( metaAnalysisObj: MetaAnalysisReturn | undefined, @@ -29,21 +25,17 @@ export const getResultStatus = ( severity: 'error', }; - if ( - metaAnalysisResult.neurovault_collection?.files && - metaAnalysisResult.neurovault_collection.files.length === 0 - ) + if (metaAnalysisResult.neurovault_collection?.files && metaAnalysisResult.neurovault_collection.files.length === 0) return { statusText: 'Detected run but no result found', color: 'warning', severity: 'warning', }; - const allFilesAreValid = ( - metaAnalysisResult.neurovault_collection.files as Array - ).every((file) => !!file.image_id); - if (!allFilesAreValid) - return { statusText: 'Latest Run Failed', color: 'error', severity: 'error' }; + const allFilesAreValid = (metaAnalysisResult.neurovault_collection.files as Array).every( + (file) => !!file.image_id + ); + if (!allFilesAreValid) return { statusText: 'Latest Run Failed', color: 'error', severity: 'error' }; if (!metaAnalysisObj?.neurostore_analysis?.neurostore_id) { return { diff --git a/compose/neurosynth-frontend/src/pages/MetaAnalysis/components/DisplayMetaAnalysisResult.tsx b/compose/neurosynth-frontend/src/pages/MetaAnalysis/components/DisplayMetaAnalysisResult.tsx index 3f26438b..e3902430 100644 --- a/compose/neurosynth-frontend/src/pages/MetaAnalysis/components/DisplayMetaAnalysisResult.tsx +++ b/compose/neurosynth-frontend/src/pages/MetaAnalysis/components/DisplayMetaAnalysisResult.tsx @@ -1,68 +1,51 @@ -import { Box, Link, Paper, Typography } from "@mui/material"; -import { - MetaAnalysisReturn, - ResultReturn, -} from "neurosynth-compose-typescript-sdk"; -import ErrorOutlineIcon from "@mui/icons-material/ErrorOutline"; -import useGetAnalysisById from "hooks/analyses/useGetAnalysisById"; -import StudyPoints from "pages/Study/components/StudyPoints"; -import { PointReturn } from "neurostore-typescript-sdk"; -import StateHandlerComponent from "components/StateHandlerComponent/StateHandlerComponent"; -import { studyPointsToStorePoints } from "pages/Study/store/StudyStore.helpers"; -import { getResultStatus } from "helpers/MetaAnalysis.helpers"; +import { Box, Link, Typography } from '@mui/material'; +import StateHandlerComponent from 'components/StateHandlerComponent/StateHandlerComponent'; +import NiiVueVisualizer from 'components/Visualizer/NiiVueVisualizer'; +import useGetAnalysisById from 'hooks/analyses/useGetAnalysisById'; +import { PointReturn } from 'neurostore-typescript-sdk'; +import { MetaAnalysisReturn, ResultReturn } from 'neurosynth-compose-typescript-sdk'; +import StudyPoints from 'pages/Study/components/StudyPoints'; +import { studyPointsToStorePoints } from 'pages/Study/store/StudyStore.helpers'; const DisplayMetaAnalysisResult: React.FC<{ metaAnalysis: MetaAnalysisReturn | undefined; metaAnalysisResult: ResultReturn | undefined; }> = (props) => { - const resultStatus = getResultStatus( - props.metaAnalysis, - props.metaAnalysisResult - ); - const { data, isLoading, isError } = useGetAnalysisById( props.metaAnalysis?.neurostore_analysis?.neurostore_id || undefined ); - const { points, analysisSpace, analysisMap } = studyPointsToStorePoints( - (data?.points || []) as PointReturn[] - ); + const { points, analysisSpace, analysisMap } = studyPointsToStorePoints((data?.points || []) as PointReturn[]); - const neurovaultLink = - props.metaAnalysisResult?.neurovault_collection?.url || ""; + const neurovaultLink = props.metaAnalysisResult?.neurovault_collection?.url || ''; return ( - - - - Result - - {resultStatus.color === "error" && ( - - - - {resultStatus.statusText} - - - )} + + + + item 1 + item 2 + item 3 + item 3 + item 3 + item 3 + item 3 + item 3 + + + + - - + + Neurovault Neurovault Collection Link @@ -76,7 +59,7 @@ const DisplayMetaAnalysisResult: React.FC<{ points={points} /> - + ); }; diff --git a/compose/neurosynth-frontend/src/pages/MetaAnalysis/components/MetaAnalysisResult.tsx b/compose/neurosynth-frontend/src/pages/MetaAnalysis/components/MetaAnalysisResult.tsx index 12f6f001..c488c31c 100644 --- a/compose/neurosynth-frontend/src/pages/MetaAnalysis/components/MetaAnalysisResult.tsx +++ b/compose/neurosynth-frontend/src/pages/MetaAnalysis/components/MetaAnalysisResult.tsx @@ -5,10 +5,10 @@ import { ResultReturn } from 'neurosynth-compose-typescript-sdk'; import { useState } from 'react'; import { useParams } from 'react-router-dom'; import DisplayMetaAnalysisResult from './DisplayMetaAnalysisResult'; -import DisplayMetaAnalysisSpecification from './MetaAnalysisSpecification'; import MetaAnalysisResultStatusAlert from './MetaAnalysisResultStatusAlert'; +import DisplayMetaAnalysisSpecification from './MetaAnalysisSpecification'; -const MetaAnalysisResult: React.FC = (props) => { +const MetaAnalysisResult: React.FC = () => { const { projectId, metaAnalysisId } = useParams<{ projectId: string; metaAnalysisId: string; diff --git a/compose/neurosynth-frontend/src/pages/Project/ProjectPage.tsx b/compose/neurosynth-frontend/src/pages/Project/ProjectPage.tsx index 69312e60..b180f0c8 100644 --- a/compose/neurosynth-frontend/src/pages/Project/ProjectPage.tsx +++ b/compose/neurosynth-frontend/src/pages/Project/ProjectPage.tsx @@ -50,11 +50,7 @@ const ProjectPage: React.FC = (props) => { const userCanEdit = useUserCanEdit(projectUser || undefined); const getProjectIsError = useProjectIsError(); - useGuard( - '/', - 'No project found with id: ' + projectId, - !getProjectIsLoading && getProjectIsError - ); + useGuard('/', 'No project found with id: ' + projectId, !getProjectIsLoading && getProjectIsError); const tab = useMemo(() => { if (!metaAnalysesTabEnabled) return 0; @@ -108,9 +104,7 @@ const ProjectPage: React.FC = (props) => { - updateProjectDescription(updatedDescription) - } + onSave={(updatedDescription, label) => updateProjectDescription(updatedDescription)} textFieldSx={{ input: { fontSize: '1.25rem' } }} textToEdit={projectDescription || ''} editIconIsVisible={userCanEdit} @@ -186,8 +180,6 @@ const ProjectPage: React.FC = (props) => { navigate(`/projects/${projectId}/project`)} sx={{ - padding: '1rem', - fontSize: '1.2rem', color: tab === 0 ? '#ef8a24 !important' : 'primary.main', fontWeight: tab === 0 ? 'bold' : 'normal', }} @@ -197,8 +189,6 @@ const ProjectPage: React.FC = (props) => { navigate(`/projects/${projectId}/meta-analyses`)} sx={{ - padding: '1rem', - fontSize: '1.2rem', color: tab === 1 ? '#ef8a24 !important' : 'primary.main', fontWeight: tab === 1 ? 'bold' : 'normal', }}