Skip to content

Commit

Permalink
add dicom viewer
Browse files Browse the repository at this point in the history
  • Loading branch information
Salam-Dalloul committed Oct 26, 2024
1 parent 3401353 commit ba70897
Show file tree
Hide file tree
Showing 25 changed files with 1,185 additions and 910 deletions.
8 changes: 3 additions & 5 deletions examples/vite-redux-toolkit-react-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,26 +25,24 @@
"@metacell/geppetto-meta-ui": "^2.0.0",
"@mui/icons-material": "^6.1.1",
"@mui/material": "^6.1.1",
"@react-three/drei": "^9.114.2",
"@react-three/fiber": "^8.17.9",
"@reduxjs/toolkit": "^2.2.7",
"@types/react-redux": "^7.1.34",
"ami.js": ">=0.32.0",
"file-saver": "^2.0.5",
"jszip": "^3.10.1",
"nifti-reader-js": "^0.6.8",
"openseadragon": "^5.0.0",
"react": "^18.3.1",
"react-color": "^2.19.3",
"react-dom": "^18.3.1",
"react-redux": "^9.1.2",
"three": "^0.169.0",
"three-render-objects": "^1.29.5"
"three": "^0.118.0",
"three-render-objects": ">=1.13.3"
},
"devDependencies": {
"@eslint/js": "^9.9.0",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@types/three": "^0.169.0",
"@vitejs/plugin-react": "^4.3.1",
"eslint": "^9.9.0",
"eslint-plugin-json": "^4.0.1",
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ import Drawer from "@mui/material/Drawer";
import { Theme } from "@mui/material/styles";
import {addWidget, deleteWidget} from "@metacell/geppetto-meta-client/common/layout/actions";
import {
componentWidget,
threeDViewerWidget,
componentWidget, DicomViewerWidget,
} from "../layoutManager/widgets.ts";
import { useDispatch } from "react-redux";
import { FormControlLabel, FormGroup } from "@mui/material";
Expand All @@ -23,7 +22,7 @@ const drawerWidth = 240;

const viewers = {
[ViewerType.default]: componentWidget(),
[ViewerType.ThreeD]: threeDViewerWidget(),
[ViewerType.dicomViewer]: DicomViewerWidget(),
};

interface LeftSidebarProps {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React, { useState } from 'react';
import DicomViewer from '@metacell/geppetto-meta-ui/dicom-viewer/preconf/DicomViewer';
import Loader from "@metacell/geppetto-meta-ui/loader/Loader";

const DicomViewerExample: React.FC = () => {
const [ready, setReady] = useState<boolean>(false);

const onLoaded = () => {
setReady(true);
};

const data = '/assets/EX_SITU_2009_UCSD_T1_WEIGHTED.nii.gz';

return <>
<Loader active={!ready}/>
<DicomViewer
id="DicomViewer"
data={data}
showDownloadButton={true}
onLoaded={onLoaded}
loaderOptions= {{ showLoader: false }}
toolbarOptions={{ innerDivStyles: { backgroundColor: 'rgb(0,0,0,0)' } }}
/>
</>
};

export default DicomViewerExample;
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import MyComponent from '../components/MyComponent'
import ThreeDViewer from "../components/viewers/ThreeD/ThreeDViewer.tsx";
import DicomViewer from "../components/viewers/Dicom/DicomViewer";

const componentMap = {
MyComponent,
ThreeDViewer,
'MyComponent': MyComponent,
'Dicom Viewer': DicomViewer
}

export default componentMap
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ export const componentWidget = () => ({
name: 'panel1',
component: "MyComponent",
panelName: 'leftPanel',
enableClose: true,
enableClose: false,
status: WidgetStatus.ACTIVE
});

export const threeDViewerWidget = () => ({
id: '3D',
name: "3D Viewer",
component: 'ThreeDViewer',
export const DicomViewerWidget = () => ({
id: 'Dicom Viewer',
name: "DicomViewer Viewer",
component: 'Dicom Viewer',
panelName: "leftPanel",
enableClose: false,
status: WidgetStatus.ACTIVE
Expand Down
4 changes: 2 additions & 2 deletions examples/vite-redux-toolkit-react-app/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import store from './redux/store'
import {Provider} from "react-redux";

createRoot(document.getElementById('root')!).render(
<StrictMode>
// <StrictMode>
<Provider store={store}>
<App />
</Provider>
</StrictMode>,
// </StrictMode>,
)
1 change: 0 additions & 1 deletion examples/vite-redux-toolkit-react-app/src/models/models.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
export enum ViewerType {
default = "Default",
ThreeD = "3D",
imageViewer= 'Image Viewer',
dicomViewer= 'Dicom Viewer',
}
1 change: 0 additions & 1 deletion examples/vite-redux-toolkit-react-app/src/redux/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ const getLayoutManagerAndStore = () => {
preloadedState: Partial<RootState>;
reducer: (state: RootState | undefined, action: Action) => RootState;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
middleware: (getDefaultMiddleware: ReturnType<ReturnType<any>>) => any;
} = {
reducer: rootReducer,
middleware: middlewareEnhancer,
Expand Down
22 changes: 21 additions & 1 deletion examples/vite-redux-toolkit-react-app/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,30 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

const gzipFixPlugin = () => {
const fixHeader = (server) => {
server.middlewares.use((req, res, next) => {
if (req.originalUrl?.includes(".gz")) {
res.setHeader("Content-Type", "gzip");
res.setHeader("Content-Encoding", "invalid-data");
}
next();
});
};

return {
name: "gzip-fix-plugin",
configureServer: fixHeader,
// vite dev and vite preview use different server, so we need to configure both.
configurePreviewServer: fixHeader,
};
};

// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
plugins: [react(), gzipFixPlugin()],
optimizeDeps: {
exclude: ['@metacell/geppetto-meta-core', '@metacell/geppetto-meta-client', '@metacell/geppetto-meta-ui']
},
assetsInclude: ['**/*.nii.gz'],
})
76 changes: 70 additions & 6 deletions geppetto-showcase/src/examples/dicom-viewer/DicomViewerExample.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,67 @@
import React, { Component } from 'react';
import DicomViewer from '@metacell/geppetto-meta-ui/dicom-viewer/DicomViewer';
import Loader from "@metacell/geppetto-meta-ui/loader/Loader";
import {
faThLarge,
faSquare,
faExchangeAlt,
faExpandAlt,
faCompressAlt,
} from '@fortawesome/free-solid-svg-icons';

export default class DicomViewerExample extends Component {
constructor (props) {
super(props);
this.state = { ready: true };
this.state = { ready: true, fullscreen: false, mode: 'quad_view', orientation: '3d' };
this.onLoaded = this.onLoaded.bind(this)

this.changeMode = this.changeMode.bind(this)
this.changeOrientation = this.changeOrientation.bind(this)
this.restore = this.restore.bind(this)
this.fullScreen = this.fullScreen.bind(this)
}

componentDidMount () {
this.setState({ ready: false });
this.setState({ ...this.state, ready: false });
}

onLoaded (){
this.setState({ ready: true });
this.setState({ ...this.state, ready: true });
}

changeMode () {
if (this.state.mode === 'single_view') {
this.setState({ ...this.state, mode: 'quad_view' });
} else {
this.setState({ ...this.state, mode: 'single_view' });
}
}

changeOrientation () {
let newOrientation;
switch (this.state.orientation) {
case 'coronal':
newOrientation = 'sagittal';
break;
case 'sagittal':
newOrientation = 'axial';
break;
case 'axial':
newOrientation = '3d';
break;
case '3d':
default:
newOrientation = 'coronal';
break;
}
this.setState({ ...this.state, orientation: newOrientation });
}

restore () {
this.setState({ ...this.state, fullScreen: false });
}

fullScreen () {
this.setState({ ...this.state, fullScreen: true });
}

render () {
Expand All @@ -36,15 +82,33 @@ export default class DicomViewerExample extends Component {
>
<DicomViewer
id="DicomViewerContainer"
mode={'quad_view'}
mode={this.state.mode}
fullScreen={this.state.fullScreen}
orientation={this.state.orientation}
data={data}
onShiftClick="goToPoint"
onCtrlClick="toggleMode"
onRightClick={event => console.log("Right click!", event)}
showDownloadButton={true}
onLoaded={this.onLoaded}
toolbarOptions={{ innerDivStyles: { backgroundColor: 'rgb(0,0,0,0);' } }}
toolbarButtons={{
single_view: [
{ icon: faThLarge, tooltip: 'Multi View', action: this.changeMode },
{ icon: faExchangeAlt, tooltip: 'Change Orientation', action: this.changeOrientation }
],
quad_view: [
{ icon: faSquare, tooltip: 'Single View', action: this.changeMode }
],
fullScreen: [
{ icon: faCompressAlt, tooltip: 'Restore', action: this.restore }
],
minimized: [
{ icon: faExpandAlt, tooltip: 'Maximize', action: this.fullScreen }
]
}}
/>
</div>
) : <Loader/>
}
}
}
7 changes: 7 additions & 0 deletions geppetto-showcase/src/pages/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const BigImgViewer = lazy(() => import(/* webpackChunkName: "bigimageviewer" */'
const Canvas = lazy(() => import(/* webpackChunkName: "canvas" */'./dataviewers/canvas'));
const ConnectivityViewer = lazy(() => import(/* webpackChunkName: "connectivityviewer" */'./dataviewers/connectivityviewer'));
const DicomViewer = lazy(() => import(/* webpackChunkName: "dicomviewer" */'./dataviewers/dicomviewer'));
const DicomViewerPreconf = lazy(() => import(/* webpackChunkName: "dicomviewerPreconf" */'./dataviewers/dicomviewerPreconf'));
const GraphVisualizer = lazy(() => import(/* webpackChunkName: "graphvisualizer" */'./dataviewers/graphvisualizer'));
const HTMLViewer = lazy(() => import(/* webpackChunkName: "htmlviewer" */'./dataviewers/htmlviewer'));
const MoviePlayer = lazy(() => import(/* webpackChunkName: "movieplayer" */'./dataviewers/movieplayer'));
Expand Down Expand Up @@ -43,6 +44,12 @@ const pages = [
name: 'Dicom Viewer',
to: '/dataviewers/dicomviewer',
},
{
component: DicomViewerPreconf,
parent: 'Data Viewers',
name: 'Preconfigured Dicom Viewer',
to: '/dataviewers/dicomviewerPreconf',
},
{
component: GraphVisualizer,
parent: 'Data Viewers',
Expand Down
25 changes: 12 additions & 13 deletions geppetto.js/geppetto-client/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@metacell/geppetto-meta-client",
"version": "3.0.0",
"version": "3.0.0-alpha.0",
"description": "Geppetto web frontend. Geppetto is an open-source platform to build web-based tools to visualize and simulate neuroscience data and models.",
"keywords": [
"geppetto",
Expand Down Expand Up @@ -30,22 +30,14 @@
"publish:yalc": "cd build && yalc publish --push",
"watch": "nodemon -e js,ts,jsx,tsx --ignore build --exec \"yarn build:dev && cd build && yalc push --changed\""
},
"dependencies": {
"@babel/plugin-proposal-class-properties": "^7.18.6",
"@material-ui/core": "^4.11.4",
"pako": "^1.0.3",
"react": "^17.0.2",
"react-redux": "^7.2.3",
"react-rnd": "^7.3.0",
"redux": "^4.1.0",
"url-join": "^4.0.0"
},
"dependencies": {},
"devDependencies": {
"@babel/cli": "^7.24.8",
"@babel/core": "^7.25.2",
"@babel/preset-env": "^7.25.3",
"@babel/preset-react": "^7.24.7",
"@babel/preset-typescript": "^7.24.7",
"@babel/plugin-proposal-class-properties": "^7.18.6",
"@eslint/js": "^9.9.0",
"babel-jest": "^29.7.0",
"babel-plugin-module-resolver": "^5.0.2",
Expand All @@ -58,8 +50,15 @@
"typescript": "^4.3.2"
},
"peerDepedencies": {
"@metacell/geppetto-meta-core": "3.0.0",
"@metacell/geppetto-meta-ui": "3.0.0"
"@metacell/geppetto-meta-core": "^3.0.0-alpha.0",
"@metacell/geppetto-meta-ui": "^3.0.0-alpha.0",
"@material-ui/core": "^4.11.4",
"pako": "^1.0.3",
"react": "^17.0.2",
"react-redux": "^7.2.3",
"react-rnd": "^7.3.0",
"redux": "^4.1.0",
"url-join": "^4.0.0"
},
"buildOptions": {
"emitEntryPoint": true,
Expand Down
2 changes: 1 addition & 1 deletion geppetto.js/geppetto-client/src/WebsocketMain.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import Events from './Events';
function createChannel () {
// Change link from blank to self for GEPPETTO_CONFIGURATION.embedded environments
if (GEPPETTO_CONFIGURATION.embedded && GEPPETTO_CONFIGURATION.embedderURL !== "/" && typeof handleRequest == 'undefined') {
if ($.isArray(GEPPETTO_CONFIGURATION.embedderURL)) {
if (Array.isArray(GEPPETTO_CONFIGURATION.embedderURL)) {
window.parent.postMessage({ "command": "ready" }, GEPPETTO_CONFIGURATION.embedderURL[0]);
} else {
window.parent.postMessage({ "command": "ready" }, GEPPETTO_CONFIGURATION.embedderURL);
Expand Down
Loading

0 comments on commit ba70897

Please sign in to comment.