Skip to content

Commit

Permalink
Merge pull request #1061 from FNNDSC/niivue-preview
Browse files Browse the repository at this point in the history
Replace cornerstone previewer with NiiVue for NIFTI
  • Loading branch information
PintoGideon authored Feb 12, 2024
2 parents 9a4d1f2 + f5be377 commit d5c8e36
Show file tree
Hide file tree
Showing 9 changed files with 109 additions and 12 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions src/api/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -326,14 +326,14 @@ export const fileViewerMap: any = {
gif: "ImageDisplay",
dcm: "DcmDisplay",
default: "CatchallDisplay",
nii: "DcmDisplay",
nii: "NiiVueDisplay",
gz: "CatchallDisplay",
mgz: "XtkDisplay",
mgz: "NiiVueDisplay",
fsm: "XtkDisplay",
crv: "XtkDisplay",
smoothwm: "XtkDisplay",
pial: "XtkDisplay",
"nii.gz": "DcmDisplay",
"nii.gz": "NiiVueDisplay",
};

// Description: get file type by file extension
Expand Down
12 changes: 12 additions & 0 deletions src/components/Preview/displays/NiiVueDisplay.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.container {
width: 100%;
height: 100%;
}


.controlBar {
position: absolute;
top: -10px;
height: 8px;

}
83 changes: 83 additions & 0 deletions src/components/Preview/displays/NiiVueDisplay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import React from "react";
import { IFileBlob } from "../../../api/model.ts";
import { NVROptions, NVRVolume } from "niivue-react/src/model.ts";
import SizedNiivueCanvas from "../../SizedNiivueCanvas";
import { SLICE_TYPE } from "@niivue/niivue";
import styles from "./NiiVueDisplay.module.css";

type NiiVueDisplayProps = {
fileItem: IFileBlob;
};

type PreviewOptions = Required<
Pick<
NVROptions,
| "sliceType"
| "isColorbar"
| "backColor"
| "isRadiologicalConvention"
| "crosshairWidth"
>
>;

const SLICE_TYPES = {
A: SLICE_TYPE.AXIAL,
C: SLICE_TYPE.CORONAL,
S: SLICE_TYPE.SAGITTAL,
M: SLICE_TYPE.MULTIPLANAR,
};

const NiiVueDisplay: React.FC<NiiVueDisplayProps> = ({ fileItem }) => {
const [colormap, setColormap] = React.useState("gray");
const [sliceTypeName, setSliceTypeName] =
React.useState<keyof typeof SLICE_TYPES>("M");

const volumes: NVRVolume[] = [];

const options: PreviewOptions = {
backColor: [0, 0, 0],
isRadiologicalConvention: true,
sliceType: SLICE_TYPES[sliceTypeName],
isColorbar: false,
crosshairWidth: sliceTypeName === "M" ? 0.5 : 0,
};

if (fileItem.blob !== undefined && fileItem.file !== undefined) {
volumes.push({
// NiiVue gets the file extension from name
name: fileItem.file.data.fname,
url: window.URL.createObjectURL(fileItem.blob),
colormap: colormap,
});
}

const toggleColormap = () => {
setColormap(colormap === "gray" ? "freesurfer" : "gray");
};

const rotateSliceType = () => {
const names = Object.keys(SLICE_TYPES) as (keyof typeof SLICE_TYPES)[];
const i = names.indexOf(sliceTypeName);
const next = i + 1 >= names.length ? 0 : i + 1;
setSliceTypeName(names[next]);
};

return (
<>
{volumes.length === 0 ? (
<h1>error</h1>
) : (
<div className={styles.container}>
<div className={styles.controlBar}>
<button onClick={toggleColormap}>{colormap}</button>
<button onClick={rotateSliceType}>{sliceTypeName}</button>
</div>
<SizedNiivueCanvas size={8} volumes={volumes} options={options} />
</div>
)}
</>
);
};

const MemoedNiiVueDisplay = React.memo(NiiVueDisplay);
export default MemoedNiiVueDisplay;
2 changes: 2 additions & 0 deletions src/components/Preview/displays/ViewerDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
PdfDisplay,
XtkDisplay,
TextDisplay,
NiiVueDisplay,
} from "./index";
import { ActionState } from "../FileDetailView";

Expand All @@ -21,6 +22,7 @@ const components = {
PdfDisplay,
XtkDisplay,
TextDisplay,
NiiVueDisplay,
};

interface ViewerDisplayProps {
Expand Down
1 change: 1 addition & 0 deletions src/components/Preview/displays/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export { default as DcmDisplay } from "./DcmDisplay";
export { default as PdfDisplay } from "./PdfDisplay";
export { default as XtkDisplay } from "./XtkDisplay";
export { default as TextDisplay } from "./TextDisplay";
export { default as NiiVueDisplay } from "./NiiVueDisplay.tsx";
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
NiivueCanvasProps,
NiivueCanvas,
} from "niivue-react/src/NiivueCanvas.tsx";
import styles from "./SizedNiivueCanvas.module.css";
import styles from "./index.module.css";

/**
* Type emitted by Niivue.onLocationChange
Expand All @@ -16,8 +16,8 @@ type CrosshairLocation = {
};

type SizedNiivueCanvasProps = NiivueCanvasProps & {
size: number;
isScaling: boolean;
size?: number;
isScaling?: boolean;
onLocationChange?: (location: CrosshairLocation) => void;
};

Expand Down Expand Up @@ -69,7 +69,7 @@ const SizedNiivueCanvas: React.FC<SizedNiivueCanvasProps> = ({
const baseTextHeight = isScaling
? 0.06
: textHeightModel(canvasWidth, canvasHeight);
const textHeight = (size / 10) * baseTextHeight;
const textHeight = ((size || 10) / 10) * baseTextHeight;
const fullOptions = options ? { ...options, textHeight } : { textHeight };

const fullOnStart = (nv: Niivue) => {
Expand Down Expand Up @@ -132,4 +132,4 @@ function textHeightModel(canvasWidth: number, canvasHeight: number): number {
}

export type { CrosshairLocation };
export { SizedNiivueCanvas };
export default SizedNiivueCanvas;
5 changes: 2 additions & 3 deletions src/components/VisualDatasets/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,9 @@ import { DEFAULT_OPTIONS } from "./defaults.ts";
import preval from "preval.macro";
import HeaderOptionBar from "./components/HeaderOptionBar.tsx";
import FeedButton from "./components/FeedButton.tsx";
import {
import SizedNiivueCanvas, {
CrosshairLocation,
SizedNiivueCanvas,
} from "./components/SizedNiivueCanvas.tsx";
} from "../SizedNiivueCanvas/index";
import { Problem, VisualDataset } from "./types.ts";
import VisualDatasetsClient from "./client.tsx";
import ProblemsManager from "./problems.ts";
Expand Down

0 comments on commit d5c8e36

Please sign in to comment.