feat: new Total Metabolic Tumor Volume tmtv (#2866)
* fix: ptDisplaySet metadata problem * fix: typo in xhrHeader field * fix: temporary fix for crosshairs on reload data * fix hotkey bug for input fields * upgrade docusarusu to stable release * circle ci node upgrade * cypress node upgrade * bump node in netlify * bump node version * remove versioned docs * update resources * fix: webpack injection of plugins * fix meaningful label for panels * add reviews and fix cli for creation * copy over chunks if exists in the build * edit templates * edit templates * fix yarn lock * fix(plugin):Deploy on windows is broken Co-authored-by: Bill Wallace <wayfarer3130@gmail.com>
This commit is contained in:
parent
ae897c47e0
commit
10b4ed5f8c
@ -18,17 +18,17 @@ executors:
|
|||||||
# Custom executor to override Cypress config
|
# Custom executor to override Cypress config
|
||||||
deploy-to-prod-executor:
|
deploy-to-prod-executor:
|
||||||
docker:
|
docker:
|
||||||
- image: cimg/node:14.18
|
- image: cimg/node:16.14
|
||||||
environment:
|
environment:
|
||||||
CYPRESS_BASE_URL: https://ohif-staging.netlify.com/
|
CYPRESS_BASE_URL: https://ohif-staging.netlify.com/
|
||||||
chrome-and-pacs:
|
chrome-and-pacs:
|
||||||
docker:
|
docker:
|
||||||
# Primary container image where all steps run.
|
# Primary container image where all steps run.
|
||||||
- image: 'cypress/browsers:node14.17.0-chrome88-ff89'
|
- image: 'cypress/browsers:node16.14.2-slim-chrome103-ff102'
|
||||||
|
|
||||||
defaults: &defaults
|
defaults: &defaults
|
||||||
docker:
|
docker:
|
||||||
- image: cimg/node:14.18-browsers
|
- image: cimg/node:16.14-browsers
|
||||||
environment:
|
environment:
|
||||||
TERM: xterm # Enable colors in term
|
TERM: xterm # Enable colors in term
|
||||||
QUICK_BUILD: true
|
QUICK_BUILD: true
|
||||||
@ -181,7 +181,7 @@ jobs:
|
|||||||
|
|
||||||
DEPLOY_TO_DEV:
|
DEPLOY_TO_DEV:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:14.17.0
|
- image: circleci/node:16.14.0
|
||||||
environment:
|
environment:
|
||||||
TERM: xterm
|
TERM: xterm
|
||||||
NETLIFY_SITE_ID: 32708787-c9b0-4634-b50f-7ca41952da77
|
NETLIFY_SITE_ID: 32708787-c9b0-4634-b50f-7ca41952da77
|
||||||
@ -196,7 +196,7 @@ jobs:
|
|||||||
|
|
||||||
DEPLOY_TO_STAGING:
|
DEPLOY_TO_STAGING:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:14.17.0
|
- image: circleci/node:16.14.0
|
||||||
environment:
|
environment:
|
||||||
TERM: xterm
|
TERM: xterm
|
||||||
NETLIFY_SITE_ID: c7502ae3-b150-493c-8422-05701e44a969
|
NETLIFY_SITE_ID: c7502ae3-b150-493c-8422-05701e44a969
|
||||||
@ -211,7 +211,7 @@ jobs:
|
|||||||
|
|
||||||
DEPLOY_TO_PRODUCTION:
|
DEPLOY_TO_PRODUCTION:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:14.17.0
|
- image: circleci/node:16.14.0
|
||||||
environment:
|
environment:
|
||||||
TERM: xterm
|
TERM: xterm
|
||||||
NETLIFY_SITE_ID: 79c4a5da-5c95-4dc9-84f7-45fd9dfe21b0
|
NETLIFY_SITE_ID: 79c4a5da-5c95-4dc9-84f7-45fd9dfe21b0
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
14.18.0
|
16.14.0
|
||||||
|
|||||||
@ -4,5 +4,6 @@
|
|||||||
"proseWrap": "always",
|
"proseWrap": "always",
|
||||||
"tabWidth": 2,
|
"tabWidth": 2,
|
||||||
"semi": true,
|
"semi": true,
|
||||||
"singleQuote": true
|
"singleQuote": true,
|
||||||
|
"arrowParens": "avoid"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,7 +45,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "7.16.3",
|
"@babel/runtime": "7.16.3",
|
||||||
"classnames": "^2.2.6",
|
"classnames": "^2.2.6",
|
||||||
"@cornerstonejs/core": "^0.13.11",
|
"@cornerstonejs/core": "^0.14.4",
|
||||||
"@cornerstonejs/tools": "^0.20.15"
|
"@cornerstonejs/tools": "^0.21.7"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,7 +29,7 @@
|
|||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@ohif/core": "^3.0.0",
|
"@ohif/core": "^3.0.0",
|
||||||
"@ohif/ui": "^2.0.0",
|
"@ohif/ui": "^2.0.0",
|
||||||
"cornerstone-wado-image-loader": "^4.2.0",
|
"cornerstone-wado-image-loader": "^4.2.1",
|
||||||
"dcmjs": "^0.24.5",
|
"dcmjs": "^0.24.5",
|
||||||
"dicom-parser": "^1.8.9",
|
"dicom-parser": "^1.8.9",
|
||||||
"hammerjs": "^2.0.8",
|
"hammerjs": "^2.0.8",
|
||||||
@ -43,9 +43,9 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "7.17.9",
|
"@babel/runtime": "7.17.9",
|
||||||
"@cornerstonejs/core": "^0.13.11",
|
"@cornerstonejs/core": "^0.14.4",
|
||||||
"@cornerstonejs/streaming-image-volume-loader": "^0.4.10",
|
"@cornerstonejs/streaming-image-volume-loader": "^0.4.16",
|
||||||
"@cornerstonejs/tools": "^0.20.15",
|
"@cornerstonejs/tools": "^0.21.7",
|
||||||
"@kitware/vtk.js": "^24.18.7",
|
"@kitware/vtk.js": "^24.18.7",
|
||||||
"dom-to-image": "^2.6.0",
|
"dom-to-image": "^2.6.0",
|
||||||
"lodash.debounce": "4.0.8",
|
"lodash.debounce": "4.0.8",
|
||||||
|
|||||||
@ -195,9 +195,11 @@ const OHIFCornerstoneViewport = React.memo(props => {
|
|||||||
DisplaySetService
|
DisplaySetService
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const keepCamera = true;
|
||||||
CornerstoneViewportService.updateViewport(
|
CornerstoneViewportService.updateViewport(
|
||||||
viewportIndex,
|
viewportIndex,
|
||||||
newViewportData
|
newViewportData,
|
||||||
|
keepCamera
|
||||||
);
|
);
|
||||||
|
|
||||||
setViewportData(newViewportData);
|
setViewportData(newViewportData);
|
||||||
|
|||||||
@ -41,7 +41,7 @@ function CornerstoneOverlays(props) {
|
|||||||
viewportIndex
|
viewportIndex
|
||||||
);
|
);
|
||||||
|
|
||||||
if (viewportInfo?.viewportOptions?.customViewportOptions?.hideOverlays) {
|
if (viewportInfo?.viewportOptions?.customViewportProps?.hideOverlays) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -115,19 +115,18 @@ const commandsModule = ({ servicesManager }) => {
|
|||||||
const renderingEngine = CornerstoneViewportService.getRenderingEngine();
|
const renderingEngine = CornerstoneViewportService.getRenderingEngine();
|
||||||
const viewport = renderingEngine.getViewport(viewportId);
|
const viewport = renderingEngine.getViewport(viewportId);
|
||||||
|
|
||||||
const lower = windowCenterNum - windowWidthNum / 2.0;
|
const { lower, upper } = csUtils.windowLevel.toLowHighRange(
|
||||||
const upper = windowCenterNum + windowWidthNum / 2.0;
|
windowWidthNum,
|
||||||
|
windowCenterNum
|
||||||
|
);
|
||||||
|
|
||||||
if (viewport instanceof StackViewport) {
|
viewport.setProperties({
|
||||||
viewport.setProperties({
|
voiRange: {
|
||||||
voiRange: {
|
upper,
|
||||||
upper,
|
lower,
|
||||||
lower,
|
},
|
||||||
},
|
});
|
||||||
});
|
viewport.render();
|
||||||
|
|
||||||
viewport.render();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
toggleCrosshairs({ toolGroupId, toggledState }) {
|
toggleCrosshairs({ toolGroupId, toggledState }) {
|
||||||
const toolName = 'Crosshairs';
|
const toolName = 'Crosshairs';
|
||||||
|
|||||||
@ -166,14 +166,12 @@ class CornerstoneViewportService implements IViewportService {
|
|||||||
const viewportInfo = this.viewportsInfo.get(viewportIndex);
|
const viewportInfo = this.viewportsInfo.get(viewportIndex);
|
||||||
viewportInfo.setRenderingEngineId(renderingEngine.id);
|
viewportInfo.setRenderingEngineId(renderingEngine.id);
|
||||||
|
|
||||||
const {
|
const { viewportOptions, displaySetOptions } =
|
||||||
viewportOptions,
|
this._getViewportAndDisplaySetOptions(
|
||||||
displaySetOptions,
|
publicViewportOptions,
|
||||||
} = this._getViewportAndDisplaySetOptions(
|
publicDisplaySetOptions,
|
||||||
publicViewportOptions,
|
viewportInfo
|
||||||
publicDisplaySetOptions,
|
);
|
||||||
viewportInfo
|
|
||||||
);
|
|
||||||
|
|
||||||
viewportInfo.setViewportOptions(viewportOptions);
|
viewportInfo.setViewportOptions(viewportOptions);
|
||||||
viewportInfo.setDisplaySetOptions(displaySetOptions);
|
viewportInfo.setDisplaySetOptions(displaySetOptions);
|
||||||
@ -400,48 +398,57 @@ class CornerstoneViewportService implements IViewportService {
|
|||||||
volume.load();
|
volume.load();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.setVolumesForViewport(viewport, volumeInputArray);
|
// This returns the async continuation only
|
||||||
|
return this.setVolumesForViewport(viewport, volumeInputArray);
|
||||||
}
|
}
|
||||||
|
|
||||||
public setVolumesForViewport(viewport, volumeInputArray) {
|
public async setVolumesForViewport(viewport, volumeInputArray) {
|
||||||
viewport.setVolumes(volumeInputArray).then(() => {
|
await viewport.setVolumes(volumeInputArray);
|
||||||
const viewportInfo = this.getViewportInfo(viewport.id);
|
|
||||||
const initialImageOptions = viewportInfo.getInitialImageOptions();
|
|
||||||
|
|
||||||
if (
|
const viewportInfo = this.getViewportInfo(viewport.id);
|
||||||
initialImageOptions &&
|
const initialImageOptions = viewportInfo.getInitialImageOptions();
|
||||||
(initialImageOptions.preset !== undefined ||
|
|
||||||
initialImageOptions.index !== undefined)
|
|
||||||
) {
|
|
||||||
const { index, preset } = initialImageOptions;
|
|
||||||
|
|
||||||
const { numberOfSlices } = csUtils.getImageSliceDataForVolumeViewport(
|
if (
|
||||||
viewport
|
initialImageOptions &&
|
||||||
);
|
(initialImageOptions.preset !== undefined ||
|
||||||
|
initialImageOptions.index !== undefined)
|
||||||
|
) {
|
||||||
|
const { index, preset } = initialImageOptions;
|
||||||
|
|
||||||
const imageIndex = this._getInitialImageIndex(
|
const { numberOfSlices } =
|
||||||
numberOfSlices,
|
csUtils.getImageSliceDataForVolumeViewport(viewport);
|
||||||
index,
|
|
||||||
preset
|
|
||||||
);
|
|
||||||
|
|
||||||
csToolsUtils.jumpToSlice(viewport.element, {
|
const imageIndex = this._getInitialImageIndex(
|
||||||
imageIndex,
|
numberOfSlices,
|
||||||
});
|
index,
|
||||||
}
|
preset
|
||||||
|
);
|
||||||
|
|
||||||
viewport.render();
|
csToolsUtils.jumpToSlice(viewport.element, {
|
||||||
});
|
imageIndex,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
viewport.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateViewport(viewportIndex, viewportData) {
|
// Todo: keepCamera is an interim solution until we have a better solution for
|
||||||
|
// keeping the camera position when the viewport data is changed
|
||||||
|
public updateViewport(viewportIndex, viewportData, keepCamera = false) {
|
||||||
const viewportInfo = this.getViewportInfoByIndex(viewportIndex);
|
const viewportInfo = this.getViewportInfoByIndex(viewportIndex);
|
||||||
|
|
||||||
const viewportId = viewportInfo.getViewportId();
|
const viewportId = viewportInfo.getViewportId();
|
||||||
const viewport = this.getCornerstoneViewport(viewportId);
|
const viewport = this.getCornerstoneViewport(viewportId);
|
||||||
|
const viewportCamera = viewport.getCamera();
|
||||||
|
|
||||||
if (viewport instanceof VolumeViewport) {
|
if (viewport instanceof VolumeViewport) {
|
||||||
this._setVolumeViewport(viewport, viewportData, viewportInfo);
|
this._setVolumeViewport(viewport, viewportData, viewportInfo).then(() => {
|
||||||
|
if (keepCamera) {
|
||||||
|
viewport.setCamera(viewportCamera);
|
||||||
|
viewport.render();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -19,7 +19,7 @@ export type ViewportOptions = {
|
|||||||
initialView?: string;
|
initialView?: string;
|
||||||
syncGroups?: SyncGroup[];
|
syncGroups?: SyncGroup[];
|
||||||
initialImageOptions?: InitialImageOptions;
|
initialImageOptions?: InitialImageOptions;
|
||||||
customViewportOptions?: Record<string, unknown>;
|
customViewportProps?: Record<string, unknown>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PublicViewportOptions = {
|
export type PublicViewportOptions = {
|
||||||
@ -31,7 +31,7 @@ export type PublicViewportOptions = {
|
|||||||
initialView?: string;
|
initialView?: string;
|
||||||
syncGroups?: SyncGroup[];
|
syncGroups?: SyncGroup[];
|
||||||
initialImageOptions?: InitialImageOptions;
|
initialImageOptions?: InitialImageOptions;
|
||||||
customViewportOptions?: Record<string, unknown>;
|
customViewportProps?: Record<string, unknown>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PublicDisplaySetOptions = {
|
export type PublicDisplaySetOptions = {
|
||||||
|
|||||||
@ -172,8 +172,8 @@ function getDisplayText(mappedAnnotations, displaySet) {
|
|||||||
|
|
||||||
// Area is the same for all series
|
// Area is the same for all series
|
||||||
const { length, width, SeriesNumber, SOPInstanceUID } = mappedAnnotations[0];
|
const { length, width, SeriesNumber, SOPInstanceUID } = mappedAnnotations[0];
|
||||||
const roundedLength = utils.roundNumber(length, 1);
|
const roundedLength = utils.roundNumber(length, 2);
|
||||||
const roundedWidth = utils.roundNumber(width, 1);
|
const roundedWidth = utils.roundNumber(width, 2);
|
||||||
|
|
||||||
const instance = displaySet.images.find(
|
const instance = displaySet.images.find(
|
||||||
image => image.SOPInstanceUID === SOPInstanceUID
|
image => image.SOPInstanceUID === SOPInstanceUID
|
||||||
|
|||||||
@ -23,7 +23,6 @@ export default function getPTImageIdInstanceMetadata(
|
|||||||
dicomMetaData.CorrectedImage === undefined ||
|
dicomMetaData.CorrectedImage === undefined ||
|
||||||
dicomMetaData.Units === undefined ||
|
dicomMetaData.Units === undefined ||
|
||||||
!dicomMetaData.RadiopharmaceuticalInformationSequence ||
|
!dicomMetaData.RadiopharmaceuticalInformationSequence ||
|
||||||
!dicomMetaData.RadiopharmaceuticalInformationSequence.length ||
|
|
||||||
dicomMetaData.RadiopharmaceuticalInformationSequence[0]
|
dicomMetaData.RadiopharmaceuticalInformationSequence[0]
|
||||||
.RadionuclideHalfLife === undefined ||
|
.RadionuclideHalfLife === undefined ||
|
||||||
dicomMetaData.RadiopharmaceuticalInformationSequence[0]
|
dicomMetaData.RadiopharmaceuticalInformationSequence[0]
|
||||||
|
|||||||
@ -36,22 +36,22 @@ const handlePETImageMetadata = ({ SeriesInstanceUID, StudyInstanceUID }) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const imageIds = instances.map(instance => instance.imageId);
|
const imageIds = instances.map(instance => instance.imageId);
|
||||||
const InstanceMetadataArray = [];
|
const instanceMetadataArray = [];
|
||||||
imageIds.forEach(imageId => {
|
imageIds.forEach(imageId => {
|
||||||
const instanceMetadata = getPTImageIdInstanceMetadata(imageId);
|
const instanceMetadata = getPTImageIdInstanceMetadata(imageId);
|
||||||
if (instanceMetadata) {
|
if (instanceMetadata) {
|
||||||
InstanceMetadataArray.push(instanceMetadata);
|
instanceMetadataArray.push(instanceMetadata);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!InstanceMetadataArray.length) {
|
if (!instanceMetadataArray.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// try except block to prevent errors when the metadata is not correct
|
// try except block to prevent errors when the metadata is not correct
|
||||||
let suvScalingFactors;
|
let suvScalingFactors;
|
||||||
try {
|
try {
|
||||||
suvScalingFactors = calculateSUVScalingFactors(InstanceMetadataArray);
|
suvScalingFactors = calculateSUVScalingFactors(instanceMetadataArray);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
@ -60,7 +60,7 @@ const handlePETImageMetadata = ({ SeriesInstanceUID, StudyInstanceUID }) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
InstanceMetadataArray.forEach((instanceMetadata, index) => {
|
instanceMetadataArray.forEach((instanceMetadata, index) => {
|
||||||
metadataProvider.addCustomMetadata(
|
metadataProvider.addCustomMetadata(
|
||||||
imageIds[index],
|
imageIds[index],
|
||||||
'scalingModule',
|
'scalingModule',
|
||||||
|
|||||||
@ -32,8 +32,8 @@
|
|||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@ohif/core": "^3.0.0",
|
"@ohif/core": "^3.0.0",
|
||||||
"classnames": "^2.2.6",
|
"classnames": "^2.2.6",
|
||||||
"@cornerstonejs/core": "^0.13.11",
|
"@cornerstonejs/core": "^0.14.4",
|
||||||
"@cornerstonejs/tools": "^0.20.15",
|
"@cornerstonejs/tools": "^0.21.7",
|
||||||
"@ohif/extension-cornerstone-dicom-sr": "^3.0.0",
|
"@ohif/extension-cornerstone-dicom-sr": "^3.0.0",
|
||||||
"dcmjs": "^0.24.5",
|
"dcmjs": "^0.24.5",
|
||||||
"prop-types": "^15.6.2",
|
"prop-types": "^15.6.2",
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import React, { useEffect, useState, useCallback } from 'react';
|
import React, { useEffect, useState, useCallback } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Input, Button } from '@ohif/ui';
|
import { Input, Button } from '@ohif/ui';
|
||||||
import { classes, DicomMetadataStore } from '@ohif/core';
|
import { DicomMetadataStore } from '@ohif/core';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
const DEFAULT_MEATADATA = {
|
const DEFAULT_MEATADATA = {
|
||||||
@ -24,7 +24,8 @@ const DEFAULT_MEATADATA = {
|
|||||||
*/
|
*/
|
||||||
export default function PanelPetSUV({ servicesManager, commandsManager }) {
|
export default function PanelPetSUV({ servicesManager, commandsManager }) {
|
||||||
const { t } = useTranslation('PanelSUV');
|
const { t } = useTranslation('PanelSUV');
|
||||||
const { DisplaySetService } = servicesManager.services;
|
const { DisplaySetService, ToolGroupService, ToolBarService } =
|
||||||
|
servicesManager.services;
|
||||||
const [metadata, setMetadata] = useState(DEFAULT_MEATADATA);
|
const [metadata, setMetadata] = useState(DEFAULT_MEATADATA);
|
||||||
const [ptDisplaySet, setPtDisplaySet] = useState(null);
|
const [ptDisplaySet, setPtDisplaySet] = useState(null);
|
||||||
|
|
||||||
@ -108,6 +109,26 @@ export default function PanelPetSUV({ servicesManager, commandsManager }) {
|
|||||||
if (!ptDisplaySet) {
|
if (!ptDisplaySet) {
|
||||||
throw new Error('No ptDisplaySet found');
|
throw new Error('No ptDisplaySet found');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const toolGroupIds = ToolGroupService.getToolGroupIds();
|
||||||
|
|
||||||
|
// Todo: we don't have a proper way to perform a toggle command and update the
|
||||||
|
// state for the toolbar, so here, we manually toggle the toolbar
|
||||||
|
|
||||||
|
// Todo: Crosshairs have bugs for the camera reset currently, so we need to
|
||||||
|
// force turn it off before we update the metadata
|
||||||
|
toolGroupIds.forEach(toolGroupId => {
|
||||||
|
commandsManager.runCommand('toggleCrosshairs', {
|
||||||
|
toolGroupId,
|
||||||
|
toggledState: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
ToolBarService.state.toggles['Crosshairs'] = false;
|
||||||
|
ToolBarService._broadcastEvent(
|
||||||
|
ToolBarService.EVENTS.TOOL_BAR_STATE_MODIFIED
|
||||||
|
);
|
||||||
|
|
||||||
// metadata should be dcmjs naturalized
|
// metadata should be dcmjs naturalized
|
||||||
DicomMetadataStore.updateMetadataForSeries(
|
DicomMetadataStore.updateMetadataForSeries(
|
||||||
ptDisplaySet.StudyInstanceUID,
|
ptDisplaySet.StudyInstanceUID,
|
||||||
@ -140,7 +161,7 @@ export default function PanelPetSUV({ servicesManager, commandsManager }) {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
label={t('Patient Weight')}
|
label={t('Patient Weight (kg)')}
|
||||||
labelClassName="text-white"
|
labelClassName="text-white"
|
||||||
className="mt-1 mb-2 bg-black border-primary-main"
|
className="mt-1 mb-2 bg-black border-primary-main"
|
||||||
type="text"
|
type="text"
|
||||||
@ -153,7 +174,7 @@ export default function PanelPetSUV({ servicesManager, commandsManager }) {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
label={t('Total Dose')}
|
label={t('Total Dose (Mbq)')}
|
||||||
labelClassName="text-white"
|
labelClassName="text-white"
|
||||||
className="mt-1 mb-2 bg-black border-primary-main"
|
className="mt-1 mb-2 bg-black border-primary-main"
|
||||||
type="text"
|
type="text"
|
||||||
@ -171,7 +192,7 @@ export default function PanelPetSUV({ servicesManager, commandsManager }) {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
label={t('Half Life')}
|
label={t('Half Life (s)')}
|
||||||
labelClassName="text-white"
|
labelClassName="text-white"
|
||||||
className="mt-1 mb-2 bg-black border-primary-main"
|
className="mt-1 mb-2 bg-black border-primary-main"
|
||||||
type="text"
|
type="text"
|
||||||
@ -189,7 +210,7 @@ export default function PanelPetSUV({ servicesManager, commandsManager }) {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
label={t('Injection Time')}
|
label={t('Injection Time (Seconds)')}
|
||||||
labelClassName="text-white"
|
labelClassName="text-white"
|
||||||
className="mt-1 mb-2 bg-black border-primary-main"
|
className="mt-1 mb-2 bg-black border-primary-main"
|
||||||
type="text"
|
type="text"
|
||||||
@ -207,7 +228,7 @@ export default function PanelPetSUV({ servicesManager, commandsManager }) {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
label={t('Acquisition Time')}
|
label={t('Acquisition Time (Seconds)')}
|
||||||
labelClassName="text-white"
|
labelClassName="text-white"
|
||||||
className="mt-1 mb-2 bg-black border-primary-main"
|
className="mt-1 mb-2 bg-black border-primary-main"
|
||||||
type="text"
|
type="text"
|
||||||
|
|||||||
@ -297,7 +297,7 @@ export default function PanelRoiThresholdSegmentation({
|
|||||||
name={'info'}
|
name={'info'}
|
||||||
className={'ml-4 mr-3 text-primary-active'}
|
className={'ml-4 mr-3 text-primary-active'}
|
||||||
/>
|
/>
|
||||||
<span>{'About'}</span>
|
<span>{'User Guide'}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -325,6 +325,12 @@ const ptCT = {
|
|||||||
source: true,
|
source: true,
|
||||||
target: true,
|
target: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'voi',
|
||||||
|
id: 'ptFusionWLSync',
|
||||||
|
source: true,
|
||||||
|
target: false,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
displaySets: [
|
displaySets: [
|
||||||
@ -360,6 +366,12 @@ const ptCT = {
|
|||||||
source: true,
|
source: true,
|
||||||
target: true,
|
target: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'voi',
|
||||||
|
id: 'ptFusionWLSync',
|
||||||
|
source: true,
|
||||||
|
target: false,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
displaySets: [
|
displaySets: [
|
||||||
@ -395,6 +407,12 @@ const ptCT = {
|
|||||||
source: true,
|
source: true,
|
||||||
target: true,
|
target: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'voi',
|
||||||
|
id: 'ptFusionWLSync',
|
||||||
|
source: true,
|
||||||
|
target: false,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
displaySets: [
|
displaySets: [
|
||||||
@ -439,6 +457,12 @@ const ptCT = {
|
|||||||
source: true,
|
source: true,
|
||||||
target: true,
|
target: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'voi',
|
||||||
|
id: 'ptFusionWLSync',
|
||||||
|
source: false,
|
||||||
|
target: true,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
displaySets: [
|
displaySets: [
|
||||||
@ -486,6 +510,12 @@ const ptCT = {
|
|||||||
source: true,
|
source: true,
|
||||||
target: true,
|
target: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'voi',
|
||||||
|
id: 'ptFusionWLSync',
|
||||||
|
source: false,
|
||||||
|
target: true,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
displaySets: [
|
displaySets: [
|
||||||
@ -533,6 +563,12 @@ const ptCT = {
|
|||||||
source: true,
|
source: true,
|
||||||
target: true,
|
target: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'voi',
|
||||||
|
id: 'ptFusionWLSync',
|
||||||
|
source: false,
|
||||||
|
target: true,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
displaySets: [
|
displaySets: [
|
||||||
@ -558,9 +594,24 @@ const ptCT = {
|
|||||||
orientation: 'sagittal',
|
orientation: 'sagittal',
|
||||||
background: [1, 1, 1],
|
background: [1, 1, 1],
|
||||||
toolGroupId: 'mipToolGroup',
|
toolGroupId: 'mipToolGroup',
|
||||||
|
syncGroups: [
|
||||||
|
{
|
||||||
|
type: 'voi',
|
||||||
|
id: 'ptWLSync',
|
||||||
|
source: true,
|
||||||
|
target: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'voi',
|
||||||
|
id: 'ptFusionWLSync',
|
||||||
|
source: true,
|
||||||
|
target: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
// Custom props can be used to set custom properties which extensions
|
// Custom props can be used to set custom properties which extensions
|
||||||
// can react on.
|
// can react on.
|
||||||
customViewportOptions: {
|
customViewportProps: {
|
||||||
// We use viewportDisplay to filter the viewports which are displayed
|
// We use viewportDisplay to filter the viewports which are displayed
|
||||||
// in mip and we set the scrollbar according to their rotation index
|
// in mip and we set the scrollbar according to their rotation index
|
||||||
// in the cornerstone extension.
|
// in the cornerstone extension.
|
||||||
|
|||||||
@ -36,14 +36,14 @@ function getPanelModule({
|
|||||||
name: 'petSUV',
|
name: 'petSUV',
|
||||||
iconName: 'edit-patient',
|
iconName: 'edit-patient',
|
||||||
iconLabel: 'PET SUV',
|
iconLabel: 'PET SUV',
|
||||||
label: 'PET-SUV',
|
label: 'PET SUV',
|
||||||
component: wrappedPanelPetSuv,
|
component: wrappedPanelPetSuv,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'ROIThresholdSeg',
|
name: 'ROIThresholdSeg',
|
||||||
iconName: 'tool-create-threshold',
|
iconName: 'tool-create-threshold',
|
||||||
iconLabel: 'Threshold Seg',
|
iconLabel: 'ROI Threshold',
|
||||||
label: 'Threshold-Seg',
|
label: 'ROI Threshold',
|
||||||
component: wrappedROIThresholdSeg,
|
component: wrappedROIThresholdSeg,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
# Total Metabolic Tumor Volume
|
# Total Metabolic Tumor Volume
|
||||||
|
|
||||||
# NOTE: THIS MODE IS STILL A WORK IN PROGRESS
|
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
Total Metabolic Tumor Volume (TMTV) workflow mode enables quantitatively measurement of a tumor volume in a patient's body.
|
Total Metabolic Tumor Volume (TMTV) workflow mode enables quantitatively measurement of a tumor volume in a patient's body.
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import toolbarButtons from './toolbarButtons.js';
|
|||||||
import { id } from './id.js';
|
import { id } from './id.js';
|
||||||
import initToolGroups, { toolGroupIds } from './initToolGroups.js';
|
import initToolGroups, { toolGroupIds } from './initToolGroups.js';
|
||||||
import setCrosshairsConfiguration from './utils/setCrosshairsConfiguration.js';
|
import setCrosshairsConfiguration from './utils/setCrosshairsConfiguration.js';
|
||||||
import setEllipticalROIConfiguration from './utils/setEllipticalROIConfiguration.js';
|
import setFusionActiveVolume from './utils/setFusionActiveVolume.js';
|
||||||
|
|
||||||
const ohif = {
|
const ohif = {
|
||||||
layout: '@ohif/extension-default.layoutTemplateModule.viewerLayout',
|
layout: '@ohif/extension-default.layoutTemplateModule.viewerLayout',
|
||||||
@ -58,7 +58,7 @@ function modeFactory({ modeConfiguration }) {
|
|||||||
// Init Default and SR ToolGroups
|
// Init Default and SR ToolGroups
|
||||||
initToolGroups(toolNames, Enums, ToolGroupService, commandsManager);
|
initToolGroups(toolNames, Enums, ToolGroupService, commandsManager);
|
||||||
|
|
||||||
const activateWindowLevel = () => {
|
const setWindowLevelActive = () => {
|
||||||
ToolBarService.recordInteraction({
|
ToolBarService.recordInteraction({
|
||||||
groupId: 'WindowLevel',
|
groupId: 'WindowLevel',
|
||||||
itemId: 'WindowLevel',
|
itemId: 'WindowLevel',
|
||||||
@ -97,7 +97,6 @@ function modeFactory({ modeConfiguration }) {
|
|||||||
const { unsubscribe } = ToolGroupService.subscribe(
|
const { unsubscribe } = ToolGroupService.subscribe(
|
||||||
ToolGroupService.EVENTS.VIEWPORT_ADDED,
|
ToolGroupService.EVENTS.VIEWPORT_ADDED,
|
||||||
() => {
|
() => {
|
||||||
activateWindowLevel();
|
|
||||||
// For fusion toolGroup we need to add the volumeIds for the crosshairs
|
// For fusion toolGroup we need to add the volumeIds for the crosshairs
|
||||||
// since in the fusion viewport we don't want both PT and CT to render MIP
|
// since in the fusion viewport we don't want both PT and CT to render MIP
|
||||||
// when slabThickness is modified
|
// when slabThickness is modified
|
||||||
@ -110,12 +109,14 @@ function modeFactory({ modeConfiguration }) {
|
|||||||
DisplaySetService
|
DisplaySetService
|
||||||
);
|
);
|
||||||
|
|
||||||
setEllipticalROIConfiguration(
|
setFusionActiveVolume(
|
||||||
matches,
|
matches,
|
||||||
toolNames,
|
toolNames,
|
||||||
ToolGroupService,
|
ToolGroupService,
|
||||||
DisplaySetService
|
DisplaySetService
|
||||||
);
|
);
|
||||||
|
|
||||||
|
setWindowLevelActive();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -67,7 +67,7 @@ function _createWwwcPreset(preset, title, subtitle) {
|
|||||||
{
|
{
|
||||||
commandName: 'setWindowLevel',
|
commandName: 'setWindowLevel',
|
||||||
commandOptions: {
|
commandOptions: {
|
||||||
windowLevel: windowLevelPresets[preset],
|
...windowLevelPresets[preset],
|
||||||
},
|
},
|
||||||
context: 'CORNERSTONE',
|
context: 'CORNERSTONE',
|
||||||
},
|
},
|
||||||
@ -272,8 +272,7 @@ const toolbarButtons = [
|
|||||||
commandName: 'displayNotification',
|
commandName: 'displayNotification',
|
||||||
commandOptions: {
|
commandOptions: {
|
||||||
title: 'RectangleROI Threshold Tip',
|
title: 'RectangleROI Threshold Tip',
|
||||||
text:
|
text: 'RectangleROI Threshold tool should be used on PT Axial Viewport',
|
||||||
'RectangleROI Threshold tool should be used on PT Axial Viewport',
|
|
||||||
type: 'info',
|
type: 'info',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -307,9 +306,10 @@ const toolbarButtons = [
|
|||||||
isAction: true, // ?
|
isAction: true, // ?
|
||||||
renderer: WindowLevelMenuItem,
|
renderer: WindowLevelMenuItem,
|
||||||
items: [
|
items: [
|
||||||
|
_createColormap('HSV', 'hsv'),
|
||||||
_createColormap('Hot Iron', 'hot_iron'),
|
_createColormap('Hot Iron', 'hot_iron'),
|
||||||
_createColormap('S PET', 's_pet'),
|
_createColormap('S PET', 's_pet'),
|
||||||
_createColormap('Ret Hot', 'red_hot'),
|
_createColormap('Red Hot', 'red_hot'),
|
||||||
_createColormap('Perfusion', 'perfusion'),
|
_createColormap('Perfusion', 'perfusion'),
|
||||||
_createColormap('Rainbow', 'rainbow_2'),
|
_createColormap('Rainbow', 'rainbow_2'),
|
||||||
_createColormap('SUV', 'suv'),
|
_createColormap('SUV', 'suv'),
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { toolGroupIds } from '../initToolGroups';
|
import { toolGroupIds } from '../initToolGroups';
|
||||||
|
|
||||||
export default function setEllipticalROIConfiguration(
|
export default function setFusionActiveVolume(
|
||||||
matches,
|
matches,
|
||||||
toolNames,
|
toolNames,
|
||||||
ToolGroupService,
|
ToolGroupService,
|
||||||
@ -22,16 +22,32 @@ export default function setEllipticalROIConfiguration(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const toolConfig = ToolGroupService.getToolConfiguration(
|
const wlToolConfig = ToolGroupService.getToolConfiguration(
|
||||||
|
toolGroupIds.Fusion,
|
||||||
|
toolNames.WindowLevel
|
||||||
|
);
|
||||||
|
|
||||||
|
const ellipticalToolConfig = ToolGroupService.getToolConfiguration(
|
||||||
toolGroupIds.Fusion,
|
toolGroupIds.Fusion,
|
||||||
toolNames.EllipticalROI
|
toolNames.EllipticalROI
|
||||||
);
|
);
|
||||||
|
|
||||||
const ellipticalROIConfig = {
|
const windowLevelConfig = {
|
||||||
...toolConfig,
|
...wlToolConfig,
|
||||||
volumeId: displaySets[0].displaySetInstanceUID,
|
volumeId: displaySets[0].displaySetInstanceUID,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const ellipticalROIConfig = {
|
||||||
|
...ellipticalToolConfig,
|
||||||
|
volumeId: displaySets[0].displaySetInstanceUID,
|
||||||
|
};
|
||||||
|
|
||||||
|
ToolGroupService.setToolConfiguration(
|
||||||
|
toolGroupIds.Fusion,
|
||||||
|
toolNames.WindowLevel,
|
||||||
|
windowLevelConfig
|
||||||
|
);
|
||||||
|
|
||||||
ToolGroupService.setToolConfiguration(
|
ToolGroupService.setToolConfiguration(
|
||||||
toolGroupIds.Fusion,
|
toolGroupIds.Fusion,
|
||||||
toolNames.EllipticalROI,
|
toolNames.EllipticalROI,
|
||||||
@ -17,7 +17,8 @@ async function editPackageJson(options) {
|
|||||||
description,
|
description,
|
||||||
author,
|
author,
|
||||||
license,
|
license,
|
||||||
files: ['dist', 'README.md'],
|
main: `dist/umd/${name}/index.umd.js`,
|
||||||
|
files: ['dist/**', 'public/**', 'README.md'],
|
||||||
},
|
},
|
||||||
packageJson
|
packageJson
|
||||||
);
|
);
|
||||||
|
|||||||
@ -17,7 +17,7 @@ function getPathQuestions(packageType) {
|
|||||||
{
|
{
|
||||||
type: 'input',
|
type: 'input',
|
||||||
name: 'baseDir',
|
name: 'baseDir',
|
||||||
message: `What is the target path to create your ${packageType} (we recommend you do not use the OHIF ${packageType} folder (./${packageType}s) unless you are developing a core ${packageType}):`,
|
message: `What is the target absolute path to create your ${packageType} (we recommend you do not use the OHIF ${packageType} folder (./${packageType}s) unless you are developing a core ${packageType}):`,
|
||||||
validate: (input) => {
|
validate: (input) => {
|
||||||
if (!input) {
|
if (!input) {
|
||||||
console.log('Please provide a valid target directory path');
|
console.log('Please provide a valid target directory path');
|
||||||
|
|||||||
@ -3,7 +3,9 @@ const pkg = require('../package.json');
|
|||||||
|
|
||||||
const outputFile = 'index.umd.js';
|
const outputFile = 'index.umd.js';
|
||||||
const rootDir = path.resolve(__dirname, '../');
|
const rootDir = path.resolve(__dirname, '../');
|
||||||
const outputFolder = path.join(__dirname, '../dist');
|
const outputFolder = path.join(__dirname, `../dist/umd/${pkg.name}/`);
|
||||||
|
|
||||||
|
// Todo: add ESM build for the extension in addition to umd build
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
mode: 'production',
|
mode: 'production',
|
||||||
@ -14,6 +16,7 @@ const config = {
|
|||||||
filename: outputFile,
|
filename: outputFile,
|
||||||
library: pkg.name,
|
library: pkg.name,
|
||||||
libraryTarget: 'umd',
|
libraryTarget: 'umd',
|
||||||
|
chunkFilename: '[name].chunk.js',
|
||||||
umdNamedDefine: true,
|
umdNamedDefine: true,
|
||||||
globalObject: "typeof self !== 'undefined' ? self : this",
|
globalObject: "typeof self !== 'undefined' ? self : this",
|
||||||
},
|
},
|
||||||
@ -46,14 +49,14 @@ const config = {
|
|||||||
loader: 'babel-loader',
|
loader: 'babel-loader',
|
||||||
exclude: /(node_modules|bower_components)/,
|
exclude: /(node_modules|bower_components)/,
|
||||||
resolve: {
|
resolve: {
|
||||||
extensions: ['.js', '.jsx', '.ts', '.tsx',],
|
extensions: ['.js', '.jsx', '.ts', '.tsx'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
modules: [path.resolve('./node_modules'), path.resolve('./src')],
|
modules: [path.resolve('./node_modules'), path.resolve('./src')],
|
||||||
extensions: ['.json', '.js', '.jsx', '.tsx', '.ts',],
|
extensions: ['.json', '.js', '.jsx', '.tsx', '.ts'],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"repository": "OHIF/Viewers",
|
"repository": "OHIF/Viewers",
|
||||||
"keywords": ["ohif-extension"],
|
"keywords": ["ohif-extension"],
|
||||||
"main": "dist/index.umd.js",
|
|
||||||
"module": "src/index.js",
|
"module": "src/index.js",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14",
|
"node": ">=14",
|
||||||
@ -51,8 +50,8 @@
|
|||||||
"dotenv": "^14.1.0",
|
"dotenv": "^14.1.0",
|
||||||
"eslint": "^5.0.1",
|
"eslint": "^5.0.1",
|
||||||
"eslint-loader": "^2.0.0",
|
"eslint-loader": "^2.0.0",
|
||||||
"uglifyjs-webpack-plugin": "^1.2.7",
|
"webpack": "^5.50.0",
|
||||||
"webpack": "^4.12.2",
|
"webpack-merge": "^5.7.3",
|
||||||
"webpack-cli": "^3.0.8"
|
"webpack-cli": "^4.7.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,8 +3,9 @@ const pkg = require('../package.json');
|
|||||||
|
|
||||||
const outputFile = 'index.umd.js';
|
const outputFile = 'index.umd.js';
|
||||||
const rootDir = path.resolve(__dirname, '../');
|
const rootDir = path.resolve(__dirname, '../');
|
||||||
const outputFolder = path.join(__dirname, '../dist');
|
const outputFolder = path.join(__dirname, `../dist/umd/${pkg.name}/`);
|
||||||
|
|
||||||
|
// Todo: add ESM build for the mode in addition to umd build
|
||||||
const config = {
|
const config = {
|
||||||
mode: 'production',
|
mode: 'production',
|
||||||
entry: rootDir + '/' + pkg.module,
|
entry: rootDir + '/' + pkg.module,
|
||||||
@ -14,6 +15,7 @@ const config = {
|
|||||||
filename: outputFile,
|
filename: outputFile,
|
||||||
library: pkg.name,
|
library: pkg.name,
|
||||||
libraryTarget: 'umd',
|
libraryTarget: 'umd',
|
||||||
|
chunkFilename: '[name].chunk.js',
|
||||||
umdNamedDefine: true,
|
umdNamedDefine: true,
|
||||||
globalObject: "typeof self !== 'undefined' ? self : this",
|
globalObject: "typeof self !== 'undefined' ? self : this",
|
||||||
},
|
},
|
||||||
@ -46,14 +48,14 @@ const config = {
|
|||||||
loader: 'babel-loader',
|
loader: 'babel-loader',
|
||||||
exclude: /(node_modules|bower_components)/,
|
exclude: /(node_modules|bower_components)/,
|
||||||
resolve: {
|
resolve: {
|
||||||
extensions: ['.js', '.jsx', '.ts', '.tsx',],
|
extensions: ['.js', '.jsx', '.ts', '.tsx'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
modules: [path.resolve('./node_modules'), path.resolve('./src')],
|
modules: [path.resolve('./node_modules'), path.resolve('./src')],
|
||||||
extensions: ['.json', '.js', '.jsx', '.tsx', '.ts',],
|
extensions: ['.json', '.js', '.jsx', '.tsx', '.ts'],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"repository": "OHIF/Viewers",
|
"repository": "OHIF/Viewers",
|
||||||
"keywords": ["ohif-mode"],
|
"keywords": ["ohif-mode"],
|
||||||
"main": "dist/index.umd.js",
|
|
||||||
"module": "src/index.js",
|
"module": "src/index.js",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14",
|
"node": ">=14",
|
||||||
@ -43,8 +42,8 @@
|
|||||||
"dotenv": "^14.1.0",
|
"dotenv": "^14.1.0",
|
||||||
"eslint": "^5.0.1",
|
"eslint": "^5.0.1",
|
||||||
"eslint-loader": "^2.0.0",
|
"eslint-loader": "^2.0.0",
|
||||||
"uglifyjs-webpack-plugin": "^1.2.7",
|
"webpack": "^5.50.0",
|
||||||
"webpack": "^4.12.2",
|
"webpack-merge": "^5.7.3",
|
||||||
"webpack-cli": "^3.0.8"
|
"webpack-cli": "^4.7.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,7 +31,7 @@
|
|||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"cornerstone-math": "0.1.9",
|
"cornerstone-math": "0.1.9",
|
||||||
"cornerstone-wado-image-loader": "^4.2.0",
|
"cornerstone-wado-image-loader": "^4.2.1",
|
||||||
"dicom-parser": "^1.8.9",
|
"dicom-parser": "^1.8.9",
|
||||||
"@ohif/ui": "^2.0.0"
|
"@ohif/ui": "^2.0.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -109,7 +109,7 @@ Below, you can find the gap analysis between the `OHIF-v2` and `OHIF-v3`:
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td align="left">Segmentation support</td>
|
<td align="left">Segmentation support</td>
|
||||||
<td align="center">🔜</td>
|
<td align="center">✅</td>
|
||||||
<td align="center">Not Started</td>
|
<td align="center">Not Started</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@ -129,7 +129,7 @@ Below, you can find the gap analysis between the `OHIF-v2` and `OHIF-v3`:
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td align="left">VTK Extension + MIP / MPR layout</td>
|
<td align="left">VTK Extension + MIP / MPR layout</td>
|
||||||
<td align="center">🔜</td>
|
<td align="center">✅</td>
|
||||||
<td align="center">3D rendering and 3D annotation tools via Cornerstone3D</td>
|
<td align="center">3D rendering and 3D annotation tools via Cornerstone3D</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@ -11,6 +11,15 @@ and other resources that we have provided to the community in the past:
|
|||||||
|
|
||||||
## 2022
|
## 2022
|
||||||
|
|
||||||
|
### OHIF Demo to Interns
|
||||||
|
[[Slides]](https://docs.google.com/presentation/d/1a2PkUnqkVMaXaBsuFn7-PPlBJULU3dBwzI_44gKFeYI/edit?usp=sharing)
|
||||||
|
|
||||||
|
### SIIM 2022 - Updates from the Imaging Informatics Community
|
||||||
|
We participated in the SIIM 2022 conference to give update for the imaging
|
||||||
|
informatics community.
|
||||||
|
[[Slides]](https://docs.google.com/presentation/d/1EUGaUzQtGhZbZWpGLe6ONqChpVMw9Qr9l3KHODevMow/edit?usp=sharing)
|
||||||
|
[[Video]](https://vimeo.com/734463662/dbd5a88371)
|
||||||
|
|
||||||
### The Imaging Network Ontario - Remote
|
### The Imaging Network Ontario - Remote
|
||||||
|
|
||||||
The Imaging Network Ontario (ImNO) is an annual symposium that brings together
|
The Imaging Network Ontario (ImNO) is an annual symposium that brings together
|
||||||
|
|||||||
@ -218,7 +218,6 @@ module.exports = {
|
|||||||
|
|
||||||
// We want users to submit doc updates to the upstream/next version!
|
// We want users to submit doc updates to the upstream/next version!
|
||||||
// Otherwise we risk losing the update on the next release.
|
// Otherwise we risk losing the update on the next release.
|
||||||
const nextVersionDocsDirPath = 'docs';
|
|
||||||
return `https://github.com/OHIF/Viewers/edit/v3-stable/platform/docs/docs/${docPath}`;
|
return `https://github.com/OHIF/Viewers/edit/v3-stable/platform/docs/docs/${docPath}`;
|
||||||
},
|
},
|
||||||
showLastUpdateAuthor: true,
|
showLastUpdateAuthor: true,
|
||||||
@ -234,11 +233,10 @@ module.exports = {
|
|||||||
// : undefined,
|
// : undefined,
|
||||||
versions: {
|
versions: {
|
||||||
current: {
|
current: {
|
||||||
label: 'Version 3.1 - Cornerstone3D 🚧',
|
label: 'Version 3.2 - Cornerstone3D Volume API 🚧',
|
||||||
},
|
},
|
||||||
'2.0': {
|
'2.0': {
|
||||||
label: 'Version 2.0 - Master branch',
|
label: 'Version 2.0 - Master branch',
|
||||||
//path: `2.0`,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@ -16,7 +16,7 @@
|
|||||||
[build.environment]
|
[build.environment]
|
||||||
# If 'production', `yarn install` does not install devDependencies
|
# If 'production', `yarn install` does not install devDependencies
|
||||||
NODE_ENV = "development"
|
NODE_ENV = "development"
|
||||||
NODE_VERSION = "14.19.1"
|
NODE_VERSION = "16.14.0"
|
||||||
YARN_VERSION = "1.22.5"
|
YARN_VERSION = "1.22.5"
|
||||||
RUBY_VERSION = "2.6.2"
|
RUBY_VERSION = "2.6.2"
|
||||||
YARN_FLAGS = "--no-ignore-optional --pure-lockfile"
|
YARN_FLAGS = "--no-ignore-optional --pure-lockfile"
|
||||||
|
|||||||
@ -27,14 +27,14 @@
|
|||||||
"write-heading-ids": "docusaurus write-heading-ids"
|
"write-heading-ids": "docusaurus write-heading-ids"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@docusaurus/core": "2.0.0-beta.20",
|
"@docusaurus/core": "2.0.1",
|
||||||
"@docusaurus/plugin-client-redirects": "2.0.0-beta.20",
|
"@docusaurus/plugin-client-redirects": "2.0.1",
|
||||||
"@docusaurus/plugin-ideal-image": "2.0.0-beta.20",
|
"@docusaurus/plugin-ideal-image": "2.0.1",
|
||||||
"@docusaurus/plugin-pwa": "2.0.0-beta.20",
|
"@docusaurus/plugin-pwa": "2.0.1",
|
||||||
"@docusaurus/preset-classic": "2.0.0-beta.20",
|
"@docusaurus/preset-classic": "2.0.1",
|
||||||
"@docusaurus/remark-plugin-npm2yarn": "2.0.0-beta.20",
|
"@docusaurus/remark-plugin-npm2yarn": "2.0.1",
|
||||||
"@docusaurus/theme-live-codeblock": "2.0.0-beta.20",
|
"@docusaurus/theme-live-codeblock": "2.0.1",
|
||||||
"@docusaurus/plugin-google-gtag": "2.0.0-beta.20",
|
"@docusaurus/plugin-google-gtag": "2.0.1",
|
||||||
"@mdx-js/react": "^1.6.21",
|
"@mdx-js/react": "^1.6.21",
|
||||||
"@svgr/webpack": "^5.5.0",
|
"@svgr/webpack": "^5.5.0",
|
||||||
"classnames": "^2.3.1",
|
"classnames": "^2.3.1",
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"Version 3.1 - Cornerstone3D Basics": "https://62e92bf05fc5050008aa1300--ohif-platform-docs.netlify.app/",
|
||||||
"Version 3.0 - Cornerstone Legacy": "https://deploy-preview-2791--ohif-platform-docs.netlify.app/",
|
"Version 3.0 - Cornerstone Legacy": "https://deploy-preview-2791--ohif-platform-docs.netlify.app/",
|
||||||
"Version 1.0": "https://deploy-preview-2791--ohif-platform-docs.netlify.app/1.0/"
|
"Version 1.0": "https://deploy-preview-2791--ohif-platform-docs.netlify.app/1.0/"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,7 +23,9 @@ class ImageScrollbar extends PureComponent {
|
|||||||
<div className="scroll">
|
<div className="scroll">
|
||||||
<div className="scroll-holder">
|
<div className="scroll-holder">
|
||||||
<input
|
<input
|
||||||
className="imageSlider"
|
// adding mousetrap let the mousetrap know about the scrollbar otherwise,
|
||||||
|
// it will not capture the keyboard event
|
||||||
|
className="imageSlider mousetrap"
|
||||||
style={this.style}
|
style={this.style}
|
||||||
type="range"
|
type="range"
|
||||||
min="0"
|
min="0"
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
const pluginConfig = require('../pluginConfig.json');
|
const pluginConfig = require('../pluginConfig.json');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
const glob = require('glob');
|
||||||
|
|
||||||
const autogenerationDisclaimer = `
|
const autogenerationDisclaimer = `
|
||||||
// THIS FILE IS AUTOGENERATED AS PART OF THE EXTENSION AND MODE PLUGIN PROCESS.
|
// THIS FILE IS AUTOGENERATED AS PART OF THE EXTENSION AND MODE PLUGIN PROCESS.
|
||||||
@ -44,11 +45,12 @@ function getFormattedImportBlock(importLines) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getFormattedWindowBlock(addToWindowLines) {
|
function getFormattedWindowBlock(addToWindowLines) {
|
||||||
let content = "const extensions = [];\n" +
|
let content =
|
||||||
"const modes = [];\n" +
|
'const extensions = [];\n' +
|
||||||
"const modesFactory = [];\n" +
|
'const modes = [];\n' +
|
||||||
"window.extensions = extensions;\n" +
|
'const modesFactory = [];\n' +
|
||||||
"window.modes = modes;\n\n";
|
'window.extensions = extensions;\n' +
|
||||||
|
'window.modes = modes;\n\n';
|
||||||
|
|
||||||
addToWindowLines.forEach(addToWindowLine => {
|
addToWindowLines.forEach(addToWindowLine => {
|
||||||
content += addToWindowLine;
|
content += addToWindowLine;
|
||||||
@ -58,34 +60,67 @@ function getFormattedWindowBlock(addToWindowLines) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getRuntimeLoadModesExtensions() {
|
function getRuntimeLoadModesExtensions() {
|
||||||
return "\n\n// Add a dynamic runtime loader\n" +
|
return (
|
||||||
"export default async () => {\n" +
|
'\n\n// Add a dynamic runtime loader\n' +
|
||||||
" for(const modeFactory of modesFactory) {\n" +
|
'export default async () => {\n' +
|
||||||
" const newModes = await modeFactory(modes,extensions);\n" +
|
' for(const modeFactory of modesFactory) {\n' +
|
||||||
" newModes.forEach(newMode => modes.push(newMode));\n" +
|
' const newModes = await modeFactory(modes,extensions);\n' +
|
||||||
"}\n}\n";
|
' newModes.forEach(newMode => modes.push(newMode));\n' +
|
||||||
|
'}\n}\n'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const createCopyPluginFromExtensions = (SRC_DIR, DIST_DIR, plugins) => {
|
const createCopyPluginToDistForLink = (
|
||||||
|
SRC_DIR,
|
||||||
|
DIST_DIR,
|
||||||
|
plugins,
|
||||||
|
folderName
|
||||||
|
) => {
|
||||||
|
return plugins
|
||||||
|
.map(plugin => {
|
||||||
|
const from = `${SRC_DIR}/../node_modules/${plugin.packageName}/${folderName}/`;
|
||||||
|
const exists = fs.existsSync(from);
|
||||||
|
return exists
|
||||||
|
? {
|
||||||
|
from,
|
||||||
|
to: DIST_DIR,
|
||||||
|
toType: 'dir',
|
||||||
|
}
|
||||||
|
: undefined;
|
||||||
|
})
|
||||||
|
.filter(x => !!x);
|
||||||
|
};
|
||||||
|
|
||||||
return plugins.map(plugin => {
|
const createCopyPluginToDistForBuild = (
|
||||||
const from = `${SRC_DIR}/../node_modules/${plugin.packageName}/public/`;
|
SRC_DIR,
|
||||||
const exists = fs.existsSync(from);
|
DIST_DIR,
|
||||||
return exists ? {
|
plugins,
|
||||||
from,
|
folderName
|
||||||
to: DIST_DIR,
|
) => {
|
||||||
toType: 'dir',
|
return plugins
|
||||||
} : undefined;
|
.map(plugin => {
|
||||||
}
|
const from = `${SRC_DIR}/../../../node_modules/${plugin.packageName}/${folderName}/`;
|
||||||
).filter(x => !!x);
|
const exists = fs.existsSync(from);
|
||||||
}
|
return exists
|
||||||
|
? {
|
||||||
|
from,
|
||||||
|
to: DIST_DIR,
|
||||||
|
toType: 'dir',
|
||||||
|
}
|
||||||
|
: undefined;
|
||||||
|
})
|
||||||
|
.filter(x => !!x);
|
||||||
|
};
|
||||||
|
|
||||||
function writePluginImportsFile(SRC_DIR, DIST_DIR) {
|
function writePluginImportsFile(SRC_DIR, DIST_DIR) {
|
||||||
let pluginImportsJsContent = autogenerationDisclaimer;
|
let pluginImportsJsContent = autogenerationDisclaimer;
|
||||||
|
|
||||||
const extensionLines = constructLines(pluginConfig.extensions, 'extensions');
|
const extensionLines = constructLines(pluginConfig.extensions, 'extensions');
|
||||||
const modeLines = constructLines(pluginConfig.modes, 'modes');
|
const modeLines = constructLines(pluginConfig.modes, 'modes');
|
||||||
const modesFactoryLines = constructLines(pluginConfig.modesFactory, 'modesFactory');
|
const modesFactoryLines = constructLines(
|
||||||
|
pluginConfig.modesFactory,
|
||||||
|
'modesFactory'
|
||||||
|
);
|
||||||
|
|
||||||
pluginImportsJsContent += getFormattedImportBlock([
|
pluginImportsJsContent += getFormattedImportBlock([
|
||||||
...extensionLines.importLines,
|
...extensionLines.importLines,
|
||||||
@ -112,17 +147,71 @@ function writePluginImportsFile(SRC_DIR, DIST_DIR) {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const copyPluginFromExtensions = createCopyPluginFromExtensions(
|
// Build packages using cli add-mode and add-extension
|
||||||
SRC_DIR, DIST_DIR,
|
// will get added to the root node_modules, but the linked packages
|
||||||
|
// will be hosted at the viewer node_modules.
|
||||||
|
|
||||||
|
const copyPluginPublicToDistBuild = createCopyPluginToDistForBuild(
|
||||||
|
SRC_DIR,
|
||||||
|
DIST_DIR,
|
||||||
[
|
[
|
||||||
...pluginConfig.modesFactory,
|
...pluginConfig.modesFactory,
|
||||||
...pluginConfig.modes,
|
...pluginConfig.modes,
|
||||||
...pluginConfig.extensions,
|
...pluginConfig.extensions,
|
||||||
...pluginConfig.umd,
|
...pluginConfig.umd,
|
||||||
]
|
],
|
||||||
)
|
'public'
|
||||||
|
);
|
||||||
|
|
||||||
return copyPluginFromExtensions;
|
const copyPluginPublicToDistLink = createCopyPluginToDistForLink(
|
||||||
|
SRC_DIR,
|
||||||
|
DIST_DIR,
|
||||||
|
[
|
||||||
|
...pluginConfig.modesFactory,
|
||||||
|
...pluginConfig.modes,
|
||||||
|
...pluginConfig.extensions,
|
||||||
|
...pluginConfig.umd,
|
||||||
|
],
|
||||||
|
'public'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Temporary way to copy chunks from the dist folder so that the become
|
||||||
|
// available
|
||||||
|
const copyPluginDistToDistBuild = createCopyPluginToDistForBuild(
|
||||||
|
SRC_DIR,
|
||||||
|
DIST_DIR,
|
||||||
|
[
|
||||||
|
...pluginConfig.modesFactory,
|
||||||
|
...pluginConfig.modes,
|
||||||
|
...pluginConfig.extensions,
|
||||||
|
...pluginConfig.umd,
|
||||||
|
],
|
||||||
|
'dist'
|
||||||
|
);
|
||||||
|
|
||||||
|
const copyPluginDistToDistLink = createCopyPluginToDistForLink(
|
||||||
|
SRC_DIR,
|
||||||
|
DIST_DIR,
|
||||||
|
[
|
||||||
|
...pluginConfig.modesFactory,
|
||||||
|
...pluginConfig.modes,
|
||||||
|
...pluginConfig.extensions,
|
||||||
|
...pluginConfig.umd,
|
||||||
|
],
|
||||||
|
'dist'
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('copy plugins', [...copyPluginPublicToDistBuild,
|
||||||
|
...copyPluginPublicToDistLink,
|
||||||
|
...copyPluginDistToDistBuild,
|
||||||
|
...copyPluginDistToDistLink,
|
||||||
|
])
|
||||||
|
return [
|
||||||
|
...copyPluginPublicToDistBuild,
|
||||||
|
...copyPluginPublicToDistLink,
|
||||||
|
...copyPluginDistToDistBuild,
|
||||||
|
...copyPluginDistToDistLink,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = writePluginImportsFile;
|
module.exports = writePluginImportsFile;
|
||||||
|
|||||||
@ -20,7 +20,7 @@
|
|||||||
[build.environment]
|
[build.environment]
|
||||||
# If 'production', `yarn install` does not install devDependencies
|
# If 'production', `yarn install` does not install devDependencies
|
||||||
NODE_ENV = "development"
|
NODE_ENV = "development"
|
||||||
NODE_VERSION = "14.19.1"
|
NODE_VERSION = "16.14.0"
|
||||||
YARN_VERSION = "1.22.5"
|
YARN_VERSION = "1.22.5"
|
||||||
RUBY_VERSION = "2.6.2"
|
RUBY_VERSION = "2.6.2"
|
||||||
YARN_FLAGS = "--no-ignore-optional --pure-lockfile"
|
YARN_FLAGS = "--no-ignore-optional --pure-lockfile"
|
||||||
|
|||||||
@ -62,7 +62,7 @@
|
|||||||
"config-point": "^0.4.8",
|
"config-point": "^0.4.8",
|
||||||
"core-js": "^3.16.1",
|
"core-js": "^3.16.1",
|
||||||
"cornerstone-math": "^0.1.9",
|
"cornerstone-math": "^0.1.9",
|
||||||
"cornerstone-wado-image-loader": "^4.2.0",
|
"cornerstone-wado-image-loader": "^4.2.1",
|
||||||
"dcmjs": "^0.24.5",
|
"dcmjs": "^0.24.5",
|
||||||
"detect-gpu": "^4.0.16",
|
"detect-gpu": "^4.0.16",
|
||||||
"dicom-parser": "^1.8.9",
|
"dicom-parser": "^1.8.9",
|
||||||
@ -90,6 +90,7 @@
|
|||||||
"cypress-file-upload": "^3.5.3",
|
"cypress-file-upload": "^3.5.3",
|
||||||
"identity-obj-proxy": "3.0.x",
|
"identity-obj-proxy": "3.0.x",
|
||||||
"lodash": "4.17.15",
|
"lodash": "4.17.15",
|
||||||
|
"glob": "^8.0.3",
|
||||||
"tailwindcss": "2.2.4",
|
"tailwindcss": "2.2.4",
|
||||||
"terser-webpack-plugin": "^5.1.1",
|
"terser-webpack-plugin": "^5.1.1",
|
||||||
"webpack": "^5.50.0",
|
"webpack": "^5.50.0",
|
||||||
|
|||||||
@ -33,10 +33,12 @@
|
|||||||
{
|
{
|
||||||
"packageName": "@ohif/mode-longitudinal",
|
"packageName": "@ohif/mode-longitudinal",
|
||||||
"version": "3.0.0"
|
"version": "3.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"packageName": "@ohif/mode-tmtv",
|
||||||
|
"version": "3.0.0"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modesFactory": [
|
"modesFactory": [],
|
||||||
],
|
"umd": []
|
||||||
"umd": [
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user