feat(measurements): add CircleROI Tool (#3211)
* [refactor] measurement service mapping files - rename to .ts files * [fix] RectangleROI - measurement service mapping _getReport() function - tool name fix * [feat] added CircleROI tool from updated cornerstone3D * [refactor] fix for typescript strong typing warnings * [feat] cornerstone-dicom-sr extension - added CircleROI tool * [feat] added toolbar button for CircleROI tool * [doc] doc updates for CircleROI tool * [update] library - dcmjs to 0.29.5 * [fix] roundNumber() function when given strings * [fix] refactor after upgrading conerstonejs library 0.49.0 * yarn.lock file change
This commit is contained in:
parent
83b4606357
commit
47c25f4364
@ -36,7 +36,7 @@
|
||||
"@ohif/extension-cornerstone": "^3.0.0",
|
||||
"@ohif/extension-measurement-tracking": "^3.0.0",
|
||||
"@ohif/ui": "^2.0.0",
|
||||
"dcmjs": "^0.29.4",
|
||||
"dcmjs": "^0.29.5",
|
||||
"dicom-parser": "^1.8.9",
|
||||
"hammerjs": "^2.0.8",
|
||||
"prop-types": "^15.6.2",
|
||||
|
||||
@ -6,6 +6,7 @@ import {
|
||||
BidirectionalTool,
|
||||
CobbAngleTool,
|
||||
EllipticalROITool,
|
||||
CircleROITool,
|
||||
LengthTool,
|
||||
PlanarFreehandROITool,
|
||||
} from '@cornerstonejs/tools';
|
||||
@ -24,6 +25,7 @@ export default function init({
|
||||
addToolInstance(toolNames.SRLength, LengthTool, {});
|
||||
addToolInstance(toolNames.SRBidirectional, BidirectionalTool);
|
||||
addToolInstance(toolNames.SREllipticalROI, EllipticalROITool);
|
||||
addToolInstance(toolNames.SRCircleROI, CircleROITool);
|
||||
addToolInstance(toolNames.SRArrowAnnotate, ArrowAnnotateTool);
|
||||
addToolInstance(toolNames.SRAngle, AngleTool);
|
||||
// TODO - fix the SR display of Cobb Angle, as it joins the two lines
|
||||
@ -40,6 +42,7 @@ export default function init({
|
||||
SRLength: dashedLine,
|
||||
SRBidirectional: dashedLine,
|
||||
SREllipticalROI: dashedLine,
|
||||
SRCircleROI: dashedLine,
|
||||
SRArrowAnnotate: dashedLine,
|
||||
SRCobbAngle: dashedLine,
|
||||
SRAngle: dashedLine,
|
||||
|
||||
@ -5,6 +5,7 @@ const toolNames = {
|
||||
SRLength: 'SRLength',
|
||||
SRBidirectional: 'SRBidirectional',
|
||||
SREllipticalROI: 'SREllipticalROI',
|
||||
SRCircleROI: 'SRCircleROI',
|
||||
SRArrowAnnotate: 'SRArrowAnnotate',
|
||||
SRAngle: 'SRAngle',
|
||||
SRCobbAngle: 'SRCobbAngle',
|
||||
|
||||
@ -30,7 +30,7 @@
|
||||
"@ohif/core": "^3.0.0",
|
||||
"@ohif/ui": "^2.0.0",
|
||||
"cornerstone-wado-image-loader": "^4.13.0",
|
||||
"dcmjs": "^0.29.4",
|
||||
"dcmjs": "^0.29.5",
|
||||
"dicom-parser": "^1.8.9",
|
||||
"hammerjs": "^2.0.8",
|
||||
"prop-types": "^15.6.2",
|
||||
|
||||
@ -9,6 +9,7 @@ import {
|
||||
LengthTool,
|
||||
RectangleROITool,
|
||||
EllipticalROITool,
|
||||
CircleROITool,
|
||||
BidirectionalTool,
|
||||
ArrowAnnotateTool,
|
||||
DragProbeTool,
|
||||
@ -41,6 +42,7 @@ export default function initCornerstoneTools(configuration = {}) {
|
||||
addTool(LengthTool);
|
||||
addTool(RectangleROITool);
|
||||
addTool(EllipticalROITool);
|
||||
addTool(CircleROITool);
|
||||
addTool(BidirectionalTool);
|
||||
addTool(ArrowAnnotateTool);
|
||||
addTool(DragProbeTool);
|
||||
@ -83,6 +85,7 @@ const toolNames = {
|
||||
Probe: ProbeTool.toolName,
|
||||
RectangleROI: RectangleROITool.toolName,
|
||||
EllipticalROI: EllipticalROITool.toolName,
|
||||
CircleROI: CircleROITool.toolName,
|
||||
Bidirectional: BidirectionalTool.toolName,
|
||||
Angle: AngleTool.toolName,
|
||||
CobbAngle: CobbAngleTool.toolName,
|
||||
|
||||
@ -24,6 +24,7 @@ const initMeasurementService = (
|
||||
Length,
|
||||
Bidirectional,
|
||||
EllipticalROI,
|
||||
CircleROI,
|
||||
ArrowAnnotate,
|
||||
Angle,
|
||||
CobbAngle,
|
||||
@ -78,6 +79,14 @@ const initMeasurementService = (
|
||||
EllipticalROI.toMeasurement
|
||||
);
|
||||
|
||||
measurementService.addMapping(
|
||||
csTools3DVer1MeasurementSource,
|
||||
'CircleROI',
|
||||
CircleROI.matchingCriteria,
|
||||
CircleROI.toAnnotation,
|
||||
CircleROI.toMeasurement
|
||||
);
|
||||
|
||||
measurementService.addMapping(
|
||||
csTools3DVer1MeasurementSource,
|
||||
'ArrowAnnotate',
|
||||
|
||||
@ -0,0 +1,228 @@
|
||||
import SUPPORTED_TOOLS from './constants/supportedTools';
|
||||
import getSOPInstanceAttributes from './utils/getSOPInstanceAttributes';
|
||||
import getModalityUnit from './utils/getModalityUnit';
|
||||
import { utils } from '@ohif/core';
|
||||
|
||||
const CircleROI = {
|
||||
toAnnotation: measurement => {},
|
||||
toMeasurement: (
|
||||
csToolsEventDetail,
|
||||
DisplaySetService,
|
||||
CornerstoneViewportService,
|
||||
getValueTypeFromToolType
|
||||
) => {
|
||||
const { annotation, viewportId } = csToolsEventDetail;
|
||||
const { metadata, data, annotationUID } = annotation;
|
||||
|
||||
if (!metadata || !data) {
|
||||
console.warn('Length tool: Missing metadata or data');
|
||||
return null;
|
||||
}
|
||||
|
||||
const { toolName, referencedImageId, FrameOfReferenceUID } = metadata;
|
||||
const validToolType = SUPPORTED_TOOLS.includes(toolName);
|
||||
|
||||
if (!validToolType) {
|
||||
throw new Error('Tool not supported');
|
||||
}
|
||||
|
||||
const {
|
||||
SOPInstanceUID,
|
||||
SeriesInstanceUID,
|
||||
StudyInstanceUID,
|
||||
} = getSOPInstanceAttributes(
|
||||
referencedImageId,
|
||||
CornerstoneViewportService,
|
||||
viewportId
|
||||
);
|
||||
|
||||
let displaySet;
|
||||
|
||||
if (SOPInstanceUID) {
|
||||
displaySet = DisplaySetService.getDisplaySetForSOPInstanceUID(
|
||||
SOPInstanceUID,
|
||||
SeriesInstanceUID
|
||||
);
|
||||
} else {
|
||||
displaySet = DisplaySetService.getDisplaySetsForSeries(SeriesInstanceUID);
|
||||
}
|
||||
|
||||
const { points } = data.handles;
|
||||
|
||||
const mappedAnnotations = getMappedAnnotations(
|
||||
annotation,
|
||||
DisplaySetService
|
||||
);
|
||||
|
||||
const displayText = getDisplayText(mappedAnnotations, displaySet);
|
||||
const getReport = () =>
|
||||
_getReport(mappedAnnotations, points, FrameOfReferenceUID);
|
||||
|
||||
return {
|
||||
uid: annotationUID,
|
||||
SOPInstanceUID,
|
||||
FrameOfReferenceUID,
|
||||
points,
|
||||
metadata,
|
||||
referenceSeriesUID: SeriesInstanceUID,
|
||||
referenceStudyUID: StudyInstanceUID,
|
||||
frameNumber: mappedAnnotations[0]?.frameNumber || 1,
|
||||
toolName: metadata.toolName,
|
||||
displaySetInstanceUID: displaySet.displaySetInstanceUID,
|
||||
label: data.label,
|
||||
displayText: displayText,
|
||||
data: data.cachedStats,
|
||||
type: getValueTypeFromToolType(toolName),
|
||||
getReport,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
function getMappedAnnotations(annotation, DisplaySetService) {
|
||||
const { metadata, data } = annotation;
|
||||
const { cachedStats } = data;
|
||||
const { referencedImageId } = metadata;
|
||||
const targets = Object.keys(cachedStats);
|
||||
|
||||
if (!targets.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const annotations = [];
|
||||
Object.keys(cachedStats).forEach(targetId => {
|
||||
const targetStats = cachedStats[targetId];
|
||||
|
||||
if (!referencedImageId) {
|
||||
// Todo: Non-acquisition plane measurement mapping not supported yet
|
||||
throw new Error(
|
||||
'Non-acquisition plane measurement mapping not supported'
|
||||
);
|
||||
}
|
||||
|
||||
const {
|
||||
SOPInstanceUID,
|
||||
SeriesInstanceUID,
|
||||
frameNumber,
|
||||
} = getSOPInstanceAttributes(referencedImageId);
|
||||
|
||||
const displaySet = DisplaySetService.getDisplaySetForSOPInstanceUID(
|
||||
SOPInstanceUID,
|
||||
SeriesInstanceUID,
|
||||
frameNumber
|
||||
);
|
||||
|
||||
const { SeriesNumber } = displaySet;
|
||||
const { mean, stdDev, max, area, Modality } = targetStats;
|
||||
const unit = getModalityUnit(Modality);
|
||||
|
||||
annotations.push({
|
||||
SeriesInstanceUID,
|
||||
SOPInstanceUID,
|
||||
SeriesNumber,
|
||||
frameNumber,
|
||||
Modality,
|
||||
unit,
|
||||
mean,
|
||||
stdDev,
|
||||
max,
|
||||
area,
|
||||
});
|
||||
});
|
||||
|
||||
return annotations;
|
||||
}
|
||||
|
||||
/*
|
||||
This function is used to convert the measurement data to a format that is
|
||||
suitable for the report generation (e.g. for the csv report). The report
|
||||
returns a list of columns and corresponding values.
|
||||
*/
|
||||
function _getReport(mappedAnnotations, points, FrameOfReferenceUID) {
|
||||
const columns = [];
|
||||
const values = [];
|
||||
|
||||
// Add Type
|
||||
columns.push('AnnotationType');
|
||||
values.push('Cornerstone:CircleROI');
|
||||
|
||||
mappedAnnotations.forEach(annotation => {
|
||||
const { mean, stdDev, max, area, unit } = annotation;
|
||||
|
||||
if (!mean || !unit || !max || !area) {
|
||||
return;
|
||||
}
|
||||
|
||||
columns.push(
|
||||
`max (${unit})`,
|
||||
`mean (${unit})`,
|
||||
`std (${unit})`,
|
||||
`area (mm2)`
|
||||
);
|
||||
values.push(max, mean, stdDev, area);
|
||||
});
|
||||
|
||||
if (FrameOfReferenceUID) {
|
||||
columns.push('FrameOfReferenceUID');
|
||||
values.push(FrameOfReferenceUID);
|
||||
}
|
||||
|
||||
if (points) {
|
||||
columns.push('points');
|
||||
// points has the form of [[x1, y1, z1], [x2, y2, z2], ...]
|
||||
// convert it to string of [[x1 y1 z1];[x2 y2 z2];...]
|
||||
// so that it can be used in the csv report
|
||||
values.push(points.map(p => p.join(' ')).join(';'));
|
||||
}
|
||||
|
||||
return {
|
||||
columns,
|
||||
values,
|
||||
};
|
||||
}
|
||||
|
||||
function getDisplayText(mappedAnnotations, displaySet) {
|
||||
if (!mappedAnnotations || !mappedAnnotations.length) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const displayText = [];
|
||||
|
||||
// Area is the same for all series
|
||||
const { area, SOPInstanceUID, frameNumber } = mappedAnnotations[0];
|
||||
|
||||
const instance = displaySet.images.find(
|
||||
image => image.SOPInstanceUID === SOPInstanceUID
|
||||
);
|
||||
|
||||
let InstanceNumber;
|
||||
if (instance) {
|
||||
InstanceNumber = instance.InstanceNumber;
|
||||
}
|
||||
|
||||
const instanceText = InstanceNumber ? ` I: ${InstanceNumber}` : '';
|
||||
const frameText = displaySet.isMultiFrame ? ` F: ${frameNumber}` : '';
|
||||
|
||||
// Area sometimes becomes undefined if `preventHandleOutsideImage` is off.
|
||||
const roundedArea = utils.roundNumber(area || 0, 2);
|
||||
displayText.push(`${roundedArea} mm<sup>2</sup>`);
|
||||
|
||||
// Todo: we need a better UI for displaying all these information
|
||||
mappedAnnotations.forEach(mappedAnnotation => {
|
||||
const { unit, max, SeriesNumber } = mappedAnnotation;
|
||||
|
||||
let maxStr = '';
|
||||
if (max) {
|
||||
const roundedMax = utils.roundNumber(max, 2);
|
||||
maxStr = `Max: ${roundedMax} <small>${unit}</small> `;
|
||||
}
|
||||
|
||||
const str = `${maxStr}(S:${SeriesNumber}${instanceText}${frameText})`;
|
||||
if (!displayText.includes(str)) {
|
||||
displayText.push(str);
|
||||
}
|
||||
});
|
||||
|
||||
return displayText;
|
||||
}
|
||||
|
||||
export default CircleROI;
|
||||
@ -1,6 +1,6 @@
|
||||
import SUPPORTED_TOOLS from './constants/supportedTools';
|
||||
import getSOPInstanceAttributes from './utils/getSOPInstanceAttributes';
|
||||
import { displaySetService, utils } from '@ohif/core';
|
||||
import { utils } from '@ohif/core';
|
||||
|
||||
const Length = {
|
||||
toAnnotation: measurement => {},
|
||||
@ -32,15 +32,12 @@ const Length = {
|
||||
throw new Error('Tool not supported');
|
||||
}
|
||||
|
||||
const {
|
||||
SOPInstanceUID,
|
||||
SeriesInstanceUID,
|
||||
StudyInstanceUID,
|
||||
} = getSOPInstanceAttributes(
|
||||
referencedImageId,
|
||||
cornerstoneViewportService,
|
||||
viewportId
|
||||
);
|
||||
const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } =
|
||||
getSOPInstanceAttributes(
|
||||
referencedImageId,
|
||||
cornerstoneViewportService,
|
||||
viewportId
|
||||
);
|
||||
|
||||
let displaySet;
|
||||
|
||||
@ -104,11 +101,8 @@ function getMappedAnnotations(annotation, displaySetService) {
|
||||
);
|
||||
}
|
||||
|
||||
const {
|
||||
SOPInstanceUID,
|
||||
SeriesInstanceUID,
|
||||
frameNumber,
|
||||
} = getSOPInstanceAttributes(referencedImageId);
|
||||
const { SOPInstanceUID, SeriesInstanceUID, frameNumber } =
|
||||
getSOPInstanceAttributes(referencedImageId);
|
||||
|
||||
const displaySet = displaySetService.getDisplaySetForSOPInstanceUID(
|
||||
SOPInstanceUID,
|
||||
@ -179,12 +173,8 @@ function getDisplayText(mappedAnnotations, displaySet) {
|
||||
const displayText = [];
|
||||
|
||||
// Area is the same for all series
|
||||
const {
|
||||
length,
|
||||
SeriesNumber,
|
||||
SOPInstanceUID,
|
||||
frameNumber,
|
||||
} = mappedAnnotations[0];
|
||||
const { length, SeriesNumber, SOPInstanceUID, frameNumber } =
|
||||
mappedAnnotations[0];
|
||||
|
||||
const instance = displaySet.images.find(
|
||||
image => image.SOPInstanceUID === SOPInstanceUID
|
||||
@ -4,7 +4,7 @@ import getModalityUnit from './utils/getModalityUnit';
|
||||
import { utils } from '@ohif/core';
|
||||
|
||||
const RectangleROI = {
|
||||
toAnnotation: measurement => { },
|
||||
toAnnotation: measurement => {},
|
||||
toMeasurement: (
|
||||
csToolsEventDetail,
|
||||
DisplaySetService,
|
||||
@ -26,15 +26,12 @@ const RectangleROI = {
|
||||
throw new Error('Tool not supported');
|
||||
}
|
||||
|
||||
const {
|
||||
SOPInstanceUID,
|
||||
SeriesInstanceUID,
|
||||
StudyInstanceUID,
|
||||
} = getSOPInstanceAttributes(
|
||||
referencedImageId,
|
||||
CornerstoneViewportService,
|
||||
viewportId
|
||||
);
|
||||
const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } =
|
||||
getSOPInstanceAttributes(
|
||||
referencedImageId,
|
||||
CornerstoneViewportService,
|
||||
viewportId
|
||||
);
|
||||
|
||||
let displaySet;
|
||||
|
||||
@ -99,11 +96,8 @@ function getMappedAnnotations(annotation, DisplaySetService) {
|
||||
);
|
||||
}
|
||||
|
||||
const {
|
||||
SOPInstanceUID,
|
||||
SeriesInstanceUID,
|
||||
frameNumber,
|
||||
} = getSOPInstanceAttributes(referencedImageId);
|
||||
const { SOPInstanceUID, SeriesInstanceUID, frameNumber } =
|
||||
getSOPInstanceAttributes(referencedImageId);
|
||||
|
||||
const displaySet = DisplaySetService.getDisplaySetForSOPInstanceUID(
|
||||
SOPInstanceUID,
|
||||
@ -143,7 +137,7 @@ function _getReport(mappedAnnotations, points, FrameOfReferenceUID) {
|
||||
|
||||
// Add Type
|
||||
columns.push('AnnotationType');
|
||||
values.push('Cornerstone:EllipticalROI');
|
||||
values.push('Cornerstone:RectangleROI');
|
||||
|
||||
mappedAnnotations.forEach(annotation => {
|
||||
const { mean, stdDev, max, area, unit } = annotation;
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
export default [
|
||||
'Length',
|
||||
'EllipticalROI',
|
||||
'CircleROI',
|
||||
'Bidirectional',
|
||||
'ArrowAnnotate',
|
||||
'Angle',
|
||||
|
||||
@ -2,6 +2,7 @@ import { MeasurementService } from '@ohif/core';
|
||||
import Length from './Length';
|
||||
import Bidirectional from './Bidirectional';
|
||||
import EllipticalROI from './EllipticalROI';
|
||||
import CircleROI from './CircleROI';
|
||||
import ArrowAnnotate from './ArrowAnnotate';
|
||||
import CobbAngle from './CobbAngle';
|
||||
import Angle from './Angle';
|
||||
@ -25,6 +26,7 @@ const measurementServiceMappingsFactory = (
|
||||
const {
|
||||
POLYLINE,
|
||||
ELLIPSE,
|
||||
CIRCLE,
|
||||
RECTANGLE,
|
||||
BIDIRECTIONAL,
|
||||
POINT,
|
||||
@ -37,6 +39,7 @@ const measurementServiceMappingsFactory = (
|
||||
const TOOL_TYPE_TO_VALUE_TYPE = {
|
||||
Length: POLYLINE,
|
||||
EllipticalROI: ELLIPSE,
|
||||
CircleROI: CIRCLE,
|
||||
RectangleROI: RECTANGLE,
|
||||
PlanarFreehandROI: POLYLINE,
|
||||
Bidirectional: BIDIRECTIONAL,
|
||||
@ -104,6 +107,22 @@ const measurementServiceMappingsFactory = (
|
||||
],
|
||||
},
|
||||
|
||||
CircleROI: {
|
||||
toAnnotation: CircleROI.toAnnotation,
|
||||
toMeasurement: csToolsAnnotation =>
|
||||
CircleROI.toMeasurement(
|
||||
csToolsAnnotation,
|
||||
displaySetService,
|
||||
cornerstoneViewportService,
|
||||
_getValueTypeFromToolType
|
||||
),
|
||||
matchingCriteria: [
|
||||
{
|
||||
valueType: MeasurementService.VALUE_TYPES.CIRCLE,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
RectangleROI: {
|
||||
toAnnotation: RectangleROI.toAnnotation,
|
||||
toMeasurement: csToolsAnnotation =>
|
||||
|
||||
@ -13,8 +13,8 @@ import * as cornerstone from '@cornerstonejs/core';
|
||||
*/
|
||||
export default function getSOPInstanceAttributes(
|
||||
imageId,
|
||||
cornerstoneViewportService,
|
||||
viewportId
|
||||
cornerstoneViewportService = undefined,
|
||||
viewportId = undefined
|
||||
) {
|
||||
if (imageId) {
|
||||
return _getUIDFromImageID(imageId);
|
||||
|
||||
@ -32,7 +32,7 @@
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "^3.0.0",
|
||||
"@ohif/i18n": "^1.0.0",
|
||||
"dcmjs": "^0.29.4",
|
||||
"dcmjs": "^0.29.5",
|
||||
"dicomweb-client": "^0.8.4",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^17.0.2",
|
||||
|
||||
@ -30,7 +30,7 @@
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "^3.0.0",
|
||||
"@ohif/ui": "^2.0.0",
|
||||
"dcmjs": "^0.29.4",
|
||||
"dcmjs": "^0.29.5",
|
||||
"dicom-parser": "^1.8.9",
|
||||
"hammerjs": "^2.0.8",
|
||||
"prop-types": "^15.6.2",
|
||||
|
||||
@ -30,7 +30,7 @@
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "^3.0.0",
|
||||
"@ohif/ui": "^2.0.0",
|
||||
"dcmjs": "^0.29.4",
|
||||
"dcmjs": "^0.29.5",
|
||||
"dicom-parser": "^1.8.9",
|
||||
"hammerjs": "^2.0.8",
|
||||
"prop-types": "^15.6.2",
|
||||
|
||||
@ -35,7 +35,7 @@
|
||||
"@cornerstonejs/core": "^0.40.0",
|
||||
"@cornerstonejs/tools": "^0.60.1",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "^3.0.0",
|
||||
"dcmjs": "^0.29.4",
|
||||
"dcmjs": "^0.29.5",
|
||||
"lodash.debounce": "^4.17.21",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^17.0.2",
|
||||
|
||||
@ -30,7 +30,7 @@
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "^3.0.0",
|
||||
"@ohif/ui": "^2.0.0",
|
||||
"dcmjs": "^0.29.4",
|
||||
"dcmjs": "^0.29.5",
|
||||
"dicom-parser": "^1.8.9",
|
||||
"hammerjs": "^2.0.8",
|
||||
"prop-types": "^15.6.2",
|
||||
|
||||
@ -81,6 +81,7 @@ function modeFactory({ modeConfiguration }) {
|
||||
{ toolName: toolNames.Bidirectional },
|
||||
{ toolName: toolNames.Probe },
|
||||
{ toolName: toolNames.EllipticalROI },
|
||||
{ toolName: toolNames.CircleROI },
|
||||
{ toolName: toolNames.RectangleROI },
|
||||
{ toolName: toolNames.StackScroll },
|
||||
{ toolName: toolNames.CalibrationLine },
|
||||
@ -134,11 +135,8 @@ function modeFactory({ modeConfiguration }) {
|
||||
]);
|
||||
},
|
||||
onModeExit: ({ servicesManager }) => {
|
||||
const {
|
||||
toolGroupService,
|
||||
measurementService,
|
||||
toolbarService,
|
||||
} = servicesManager.services;
|
||||
const { toolGroupService, measurementService, toolbarService } =
|
||||
servicesManager.services;
|
||||
|
||||
toolGroupService.destroy();
|
||||
},
|
||||
|
||||
@ -130,6 +130,21 @@ const toolbarButtons = [
|
||||
],
|
||||
'Ellipse Tool'
|
||||
),
|
||||
_createToolButton(
|
||||
'CircleROI',
|
||||
'tool-circle',
|
||||
'Circle',
|
||||
[
|
||||
{
|
||||
commandName: 'setToolActive',
|
||||
commandOptions: {
|
||||
toolName: 'CircleROI',
|
||||
},
|
||||
context: 'CORNERSTONE',
|
||||
},
|
||||
],
|
||||
'Circle Tool'
|
||||
),
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
@ -32,6 +32,7 @@ function initDefaultToolGroup(
|
||||
{ toolName: toolNames.Bidirectional },
|
||||
{ toolName: toolNames.DragProbe },
|
||||
{ toolName: toolNames.EllipticalROI },
|
||||
{ toolName: toolNames.CircleROI },
|
||||
{ toolName: toolNames.RectangleROI },
|
||||
{ toolName: toolNames.StackScroll },
|
||||
{ toolName: toolNames.Angle },
|
||||
@ -110,6 +111,7 @@ function initSRToolGroup(extensionManager, toolGroupService, commandsManager) {
|
||||
{ toolName: SRToolNames.SRArrowAnnotate },
|
||||
{ toolName: SRToolNames.SRBidirectional },
|
||||
{ toolName: SRToolNames.SREllipticalROI },
|
||||
{ toolName: SRToolNames.SRCircleROI },
|
||||
],
|
||||
enabled: [
|
||||
{
|
||||
@ -170,6 +172,7 @@ function initMPRToolGroup(extensionManager, toolGroupService, commandsManager) {
|
||||
{ toolName: toolNames.Bidirectional },
|
||||
{ toolName: toolNames.DragProbe },
|
||||
{ toolName: toolNames.EllipticalROI },
|
||||
{ toolName: toolNames.CircleROI },
|
||||
{ toolName: toolNames.RectangleROI },
|
||||
{ toolName: toolNames.StackScroll },
|
||||
{ toolName: toolNames.Angle },
|
||||
|
||||
@ -200,6 +200,29 @@ const toolbarButtons = [
|
||||
],
|
||||
'Ellipse Tool'
|
||||
),
|
||||
_createToolButton(
|
||||
'CircleROI',
|
||||
'tool-circle',
|
||||
'Circle',
|
||||
[
|
||||
{
|
||||
commandName: 'setToolActive',
|
||||
commandOptions: {
|
||||
toolName: 'CircleROI',
|
||||
},
|
||||
context: 'CORNERSTONE',
|
||||
},
|
||||
{
|
||||
commandName: 'setToolActive',
|
||||
commandOptions: {
|
||||
toolName: 'SRCircleROI',
|
||||
toolGroupId: 'SRToolGroup',
|
||||
},
|
||||
context: 'CORNERSTONE',
|
||||
},
|
||||
],
|
||||
'Circle Tool'
|
||||
),
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
@ -32,6 +32,7 @@ function initDefaultToolGroup(
|
||||
{ toolName: toolNames.Bidirectional },
|
||||
{ toolName: toolNames.DragProbe },
|
||||
{ toolName: toolNames.EllipticalROI },
|
||||
{ toolName: toolNames.CircleROI },
|
||||
{ toolName: toolNames.RectangleROI },
|
||||
{ toolName: toolNames.StackScroll },
|
||||
{ toolName: toolNames.Angle },
|
||||
@ -113,6 +114,7 @@ function initSRToolGroup(extensionManager, toolGroupService, commandsManager) {
|
||||
{ toolName: SRToolNames.SRArrowAnnotate },
|
||||
{ toolName: SRToolNames.SRBidirectional },
|
||||
{ toolName: SRToolNames.SREllipticalROI },
|
||||
{ toolName: SRToolNames.SRCircleROI },
|
||||
],
|
||||
enabled: [
|
||||
{
|
||||
@ -173,6 +175,7 @@ function initMPRToolGroup(extensionManager, toolGroupService, commandsManager) {
|
||||
{ toolName: toolNames.Bidirectional },
|
||||
{ toolName: toolNames.DragProbe },
|
||||
{ toolName: toolNames.EllipticalROI },
|
||||
{ toolName: toolNames.CircleROI },
|
||||
{ toolName: toolNames.RectangleROI },
|
||||
{ toolName: toolNames.StackScroll },
|
||||
{ toolName: toolNames.Angle },
|
||||
|
||||
@ -208,6 +208,29 @@ const toolbarButtons = [
|
||||
],
|
||||
'Ellipse Tool'
|
||||
),
|
||||
_createToolButton(
|
||||
'CircleROI',
|
||||
'tool-circle',
|
||||
'Circle',
|
||||
[
|
||||
{
|
||||
commandName: 'setToolActive',
|
||||
commandOptions: {
|
||||
toolName: 'CircleROI',
|
||||
},
|
||||
context: 'CORNERSTONE',
|
||||
},
|
||||
{
|
||||
commandName: 'setToolActive',
|
||||
commandOptions: {
|
||||
toolName: 'SRCircleROI',
|
||||
toolGroupId: 'SRToolGroup',
|
||||
},
|
||||
context: 'CORNERSTONE',
|
||||
},
|
||||
],
|
||||
'Circle Tool'
|
||||
),
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
@ -173,6 +173,20 @@ const toolbarButtons = [
|
||||
],
|
||||
'Ellipse Tool'
|
||||
),
|
||||
_createToolButton(
|
||||
'CircleROI',
|
||||
'tool-circle',
|
||||
'Circle',
|
||||
[
|
||||
..._createCommands('setToolActive', 'CircleROI', [
|
||||
toolGroupIds.CT,
|
||||
toolGroupIds.PT,
|
||||
toolGroupIds.Fusion,
|
||||
// toolGroupIds.MPR,
|
||||
]),
|
||||
],
|
||||
'Circle Tool'
|
||||
),
|
||||
],
|
||||
},
|
||||
},
|
||||
@ -299,8 +313,7 @@ const toolbarButtons = [
|
||||
commandName: 'displayNotification',
|
||||
commandOptions: {
|
||||
title: 'RectangleROI Threshold Tip',
|
||||
text:
|
||||
'RectangleROI Threshold tool should be used on PT Axial Viewport',
|
||||
text: 'RectangleROI Threshold tool should be used on PT Axial Viewport',
|
||||
type: 'info',
|
||||
},
|
||||
},
|
||||
|
||||
@ -37,7 +37,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"dcmjs": "^0.29.4",
|
||||
"dcmjs": "^0.29.5",
|
||||
"dicomweb-client": "^0.8.4",
|
||||
"isomorphic-base64": "^1.0.2",
|
||||
"lodash.merge": "^4.6.1",
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
/**
|
||||
* @param {string | number} value
|
||||
* @param {number} decimals
|
||||
*/
|
||||
function _round(value, decimals) {
|
||||
return Number(value.toFixed(decimals));
|
||||
return Number(value).toFixed(decimals);
|
||||
}
|
||||
|
||||
export default _round;
|
||||
|
||||
@ -184,6 +184,16 @@ to create `MeasurementTools` nested button
|
||||
toolName: 'EllipticalRoi',
|
||||
}
|
||||
},
|
||||
// Circle tool
|
||||
{
|
||||
id: 'CircleROI',
|
||||
icon: 'tool-circle',
|
||||
label: 'Circle',
|
||||
type: 'tool',
|
||||
commandOptions: {
|
||||
toolName: 'CircleROI',
|
||||
}
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
@ -210,6 +210,16 @@ to create `MeasurementTools` nested button
|
||||
toolName: 'EllipticalRoi',
|
||||
}
|
||||
},
|
||||
// Circle tool
|
||||
{
|
||||
id: 'CircleROI',
|
||||
icon: 'tool-circle',
|
||||
label: 'Circle',
|
||||
type: 'tool',
|
||||
commandOptions: {
|
||||
toolName: 'CircleROI',
|
||||
}
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
3
platform/ui/src/assets/icons/tool-circle.svg
Normal file
3
platform/ui/src/assets/icons/tool-circle.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||
<circle stroke="currentColor" fill="none" stroke-width="2" cx="16" cy="16" r="14" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 157 B |
@ -81,6 +81,7 @@ import toolWindow from './../../assets/icons/tool-window-level.svg';
|
||||
import toolAnnotate from './../../assets/icons/tool-annotate.svg';
|
||||
import toolBidirectional from './../../assets/icons/tool-bidirectional.svg';
|
||||
import toolElipse from './../../assets/icons/tool-elipse.svg';
|
||||
import toolCircle from './../../assets/icons/tool-circle.svg';
|
||||
import toolLength from './../../assets/icons/tool-length.svg';
|
||||
import toolStackScroll from './../../assets/icons/tool-stack-scroll.svg';
|
||||
import toolMagnify from './../../assets/icons/tool-magnify.svg';
|
||||
@ -199,6 +200,7 @@ const ICONS = {
|
||||
'tool-annotate': toolAnnotate,
|
||||
'tool-bidirectional': toolBidirectional,
|
||||
'tool-elipse': toolElipse,
|
||||
'tool-circle': toolCircle,
|
||||
'tool-length': toolLength,
|
||||
'tool-stack-scroll': toolStackScroll,
|
||||
'tool-magnify': toolMagnify,
|
||||
|
||||
@ -66,7 +66,7 @@
|
||||
"core-js": "^3.16.1",
|
||||
"cornerstone-math": "^0.1.9",
|
||||
"cornerstone-wado-image-loader": "^4.13.0",
|
||||
"dcmjs": "^0.29.4",
|
||||
"dcmjs": "^0.29.5",
|
||||
"detect-gpu": "^4.0.16",
|
||||
"dicom-parser": "^1.8.9",
|
||||
"dotenv-webpack": "^1.7.0",
|
||||
|
||||
14
yarn.lock
14
yarn.lock
@ -9247,21 +9247,9 @@ dayjs@^1.10.4:
|
||||
resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz#4b296922642f70999544d1144a2c25730fce63e2"
|
||||
integrity sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==
|
||||
|
||||
dcmjs@^0.29.4:
|
||||
version "0.29.4"
|
||||
resolved "https://registry.npmjs.org/dcmjs/-/dcmjs-0.29.4.tgz#3fb13945611979f756bc91b3f199fd87b4eabdb4"
|
||||
integrity sha512-PzD6C4oB7v/AVTIPbkS6D6hjgVcSW7+T+DsxFpkD1xcEgU4qjGM16WGEHH3ZAPDBF0Xgm2d9FXUu/lXZIyfmow==
|
||||
dependencies:
|
||||
"@babel/runtime-corejs2" "^7.17.8"
|
||||
gl-matrix "^3.1.0"
|
||||
lodash.clonedeep "^4.5.0"
|
||||
loglevelnext "^3.0.1"
|
||||
ndarray "^1.0.19"
|
||||
pako "^2.0.4"
|
||||
|
||||
dcmjs@^0.29.5:
|
||||
version "0.29.5"
|
||||
resolved "https://registry.npmjs.org/dcmjs/-/dcmjs-0.29.5.tgz#3e2311fc47aafc70f21e236ad89e2ff9ba6589fd"
|
||||
resolved "https://registry.yarnpkg.com/dcmjs/-/dcmjs-0.29.5.tgz#3e2311fc47aafc70f21e236ad89e2ff9ba6589fd"
|
||||
integrity sha512-CcLo3pwitf9JhhvW3/icCraxUhaIKJRyOys9XJlBBDn5TQskyJzxgveLKejcryHn4DooqlJPeMuM4ixdPFEERQ==
|
||||
dependencies:
|
||||
"@babel/runtime-corejs2" "^7.17.8"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user