@cornerstonejs/tools 5.5.1 → 5.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/index.js +2 -2
- package/dist/esm/tools/annotation/BidirectionalTool.js +0 -1
- package/dist/esm/tools/annotation/CircleROITool.d.ts +4 -1
- package/dist/esm/tools/annotation/CircleROITool.js +34 -34
- package/dist/esm/tools/annotation/DragProbeTool.js +1 -1
- package/dist/esm/tools/annotation/RectangleROITool.d.ts +2 -2
- package/dist/esm/tools/annotation/RectangleROITool.js +23 -49
- package/dist/esm/tools/base/AnnotationTool.js +6 -4
- package/dist/esm/tools/base/BaseTool.d.ts +15 -4
- package/dist/esm/tools/base/BaseTool.js +127 -6
- package/dist/esm/tools/base/index.d.ts +3 -1
- package/dist/esm/tools/base/index.js +3 -1
- package/dist/esm/tools/base/measurementTargetFilters.d.ts +19 -0
- package/dist/esm/tools/base/measurementTargetFilters.js +38 -0
- package/dist/esm/tools/index.d.ts +2 -2
- package/dist/esm/tools/index.js +2 -2
- package/dist/esm/tools/segmentation/CircleROIStartEndThresholdTool.d.ts +2 -40
- package/dist/esm/tools/segmentation/CircleROIStartEndThresholdTool.js +1 -2
- package/dist/esm/tools/segmentation/RectangleROIStartEndThresholdTool.d.ts +0 -1
- package/dist/esm/tools/segmentation/RectangleROIStartEndThresholdTool.js +0 -1
- package/dist/esm/types/AnnotationTypes.d.ts +34 -1
- package/dist/esm/types/IBaseTool.d.ts +22 -0
- package/dist/esm/types/ToolSpecificAnnotationTypes.d.ts +26 -53
- package/dist/esm/types/index.d.ts +2 -2
- package/dist/esm/utilities/defaultGetTextLines.d.ts +9 -0
- package/dist/esm/utilities/defaultGetTextLines.js +53 -0
- package/dist/esm/utilities/getEllipseWorldCoordinates.d.ts +2 -2
- package/dist/esm/utilities/getEllipseWorldCoordinates.js +2 -2
- package/dist/esm/utilities/index.d.ts +1 -0
- package/dist/esm/utilities/index.js +1 -0
- package/dist/esm/utilities/planar/filterAnnotationsWithinSlice.js +1 -1
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/package.json +5 -5
package/dist/esm/index.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ import * as Types from './types/index.js';
|
|
|
12
12
|
import * as annotation from './stateManagement/annotation/index.js';
|
|
13
13
|
import * as segmentation from './stateManagement/segmentation/index.js';
|
|
14
14
|
import * as splines from './tools/annotation/splines/index.js';
|
|
15
|
-
import { BaseTool, AnnotationTool, AnnotationDisplayTool, PanTool, TrackballRotateTool, VolumeCroppingTool, VolumeCroppingControlTool, DragProbeTool, WindowLevelTool, ZoomTool, StackScrollTool, SegmentBidirectionalTool, PlanarRotateTool, MIPJumpToClickTool, LabelTool, LengthTool, HeightTool, ProbeTool, RectangleROITool, EllipticalROITool, CircleROITool, ETDRSGridTool, SplineROITool, SplineContourSegmentationTool, BidirectionalTool, PlanarFreehandROITool, PlanarFreehandContourSegmentationTool, LivewireContourTool, LivewireContourSegmentationTool, ArrowAnnotateTool, KeyImageTool, CrosshairsTool, WorldCrosshairTool, SliceIntersectionTool, ReferenceLinesTool, RectangleScissorsTool, CircleScissorsTool, SphereScissorsTool, RectangleROIThresholdTool, RectangleROIStartEndThresholdTool, CircleROIStartEndThresholdTool, BrushTool, AngleTool, CobbAngleTool, UltrasoundDirectionalTool, UltrasoundPleuraBLineTool, MagnifyTool, AdvancedMagnifyTool, ReferenceCursors, PaintFillTool, ScaleOverlayTool, OrientationMarkerTool, OrientationControllerTool, OverlayGridTool, SegmentationIntersectionTool, EraserTool, SculptorTool, SegmentSelectTool, WindowLevelRegionTool, VolumeRotateTool, RegionSegmentPlusTool, RegionSegmentTool, ClickSegmentTool, WholeBodySegmentTool, LabelmapBaseTool, SegmentLabelTool, LabelMapEditWithContourTool } from './tools/index.js';
|
|
15
|
+
import { BaseTool, AnnotationTool, AnnotationDisplayTool, measurementTargetFilters, PanTool, TrackballRotateTool, VolumeCroppingTool, VolumeCroppingControlTool, DragProbeTool, WindowLevelTool, ZoomTool, StackScrollTool, SegmentBidirectionalTool, PlanarRotateTool, MIPJumpToClickTool, LabelTool, LengthTool, HeightTool, ProbeTool, RectangleROITool, EllipticalROITool, CircleROITool, ETDRSGridTool, SplineROITool, SplineContourSegmentationTool, BidirectionalTool, PlanarFreehandROITool, PlanarFreehandContourSegmentationTool, LivewireContourTool, LivewireContourSegmentationTool, ArrowAnnotateTool, KeyImageTool, CrosshairsTool, WorldCrosshairTool, SliceIntersectionTool, ReferenceLinesTool, RectangleScissorsTool, CircleScissorsTool, SphereScissorsTool, RectangleROIThresholdTool, RectangleROIStartEndThresholdTool, CircleROIStartEndThresholdTool, BrushTool, AngleTool, CobbAngleTool, UltrasoundDirectionalTool, UltrasoundPleuraBLineTool, MagnifyTool, AdvancedMagnifyTool, ReferenceCursors, PaintFillTool, ScaleOverlayTool, OrientationMarkerTool, OrientationControllerTool, OverlayGridTool, SegmentationIntersectionTool, EraserTool, SculptorTool, SegmentSelectTool, WindowLevelRegionTool, VolumeRotateTool, RegionSegmentPlusTool, RegionSegmentTool, ClickSegmentTool, WholeBodySegmentTool, LabelmapBaseTool, SegmentLabelTool, LabelMapEditWithContourTool } from './tools/index.js';
|
|
16
16
|
import VideoRedactionTool from './tools/annotation/VideoRedactionTool.js';
|
|
17
17
|
import * as Enums from './enums/index.js';
|
|
18
|
-
export { VideoRedactionTool, init, destroy, addTool, removeTool, cancelActiveManipulations, BaseTool, AnnotationTool, AnnotationDisplayTool, PanTool, SegmentBidirectionalTool, TrackballRotateTool, VolumeCroppingTool, VolumeCroppingControlTool, DragProbeTool, WindowLevelTool, WindowLevelRegionTool, ZoomTool, StackScrollTool, PlanarRotateTool, MIPJumpToClickTool, LabelTool, LengthTool, HeightTool, CrosshairsTool, WorldCrosshairTool, SliceIntersectionTool, ReferenceLinesTool, OverlayGridTool, SegmentationIntersectionTool, ProbeTool, RectangleROITool, EllipticalROITool, CircleROITool, ETDRSGridTool, SplineROITool, SplineContourSegmentationTool, BidirectionalTool, PlanarFreehandROITool, PlanarFreehandContourSegmentationTool, LivewireContourTool, LivewireContourSegmentationTool, ArrowAnnotateTool, AngleTool, CobbAngleTool, UltrasoundDirectionalTool, UltrasoundPleuraBLineTool, KeyImageTool, MagnifyTool, AdvancedMagnifyTool, ReferenceCursors, ScaleOverlayTool, SculptorTool, EraserTool, RectangleScissorsTool, CircleScissorsTool, SphereScissorsTool, RectangleROIThresholdTool, RectangleROIStartEndThresholdTool, CircleROIStartEndThresholdTool, BrushTool, OrientationMarkerTool, OrientationControllerTool, SegmentSelectTool, SegmentLabelTool, synchronizers, Synchronizer, SynchronizerManager, PaintFillTool, Types, state, ToolGroupManager, store, Enums, CONSTANTS, drawing, annotation, segmentation, utilities, cursors, VolumeRotateTool, RegionSegmentPlusTool, RegionSegmentTool, ClickSegmentTool, WholeBodySegmentTool, LabelmapBaseTool, LabelMapEditWithContourTool, splines, version, };
|
|
18
|
+
export { VideoRedactionTool, init, destroy, addTool, removeTool, cancelActiveManipulations, BaseTool, AnnotationTool, AnnotationDisplayTool, measurementTargetFilters, PanTool, SegmentBidirectionalTool, TrackballRotateTool, VolumeCroppingTool, VolumeCroppingControlTool, DragProbeTool, WindowLevelTool, WindowLevelRegionTool, ZoomTool, StackScrollTool, PlanarRotateTool, MIPJumpToClickTool, LabelTool, LengthTool, HeightTool, CrosshairsTool, WorldCrosshairTool, SliceIntersectionTool, ReferenceLinesTool, OverlayGridTool, SegmentationIntersectionTool, ProbeTool, RectangleROITool, EllipticalROITool, CircleROITool, ETDRSGridTool, SplineROITool, SplineContourSegmentationTool, BidirectionalTool, PlanarFreehandROITool, PlanarFreehandContourSegmentationTool, LivewireContourTool, LivewireContourSegmentationTool, ArrowAnnotateTool, AngleTool, CobbAngleTool, UltrasoundDirectionalTool, UltrasoundPleuraBLineTool, KeyImageTool, MagnifyTool, AdvancedMagnifyTool, ReferenceCursors, ScaleOverlayTool, SculptorTool, EraserTool, RectangleScissorsTool, CircleScissorsTool, SphereScissorsTool, RectangleROIThresholdTool, RectangleROIStartEndThresholdTool, CircleROIStartEndThresholdTool, BrushTool, OrientationMarkerTool, OrientationControllerTool, SegmentSelectTool, SegmentLabelTool, synchronizers, Synchronizer, SynchronizerManager, PaintFillTool, Types, state, ToolGroupManager, store, Enums, CONSTANTS, drawing, annotation, segmentation, utilities, cursors, VolumeRotateTool, RegionSegmentPlusTool, RegionSegmentTool, ClickSegmentTool, WholeBodySegmentTool, LabelmapBaseTool, LabelMapEditWithContourTool, splines, version, };
|
package/dist/esm/index.js
CHANGED
|
@@ -12,7 +12,7 @@ import * as Types from './types/index.js';
|
|
|
12
12
|
import * as annotation from './stateManagement/annotation/index.js';
|
|
13
13
|
import * as segmentation from './stateManagement/segmentation/index.js';
|
|
14
14
|
import * as splines from './tools/annotation/splines/index.js';
|
|
15
|
-
import { BaseTool, AnnotationTool, AnnotationDisplayTool, PanTool, TrackballRotateTool, VolumeCroppingTool, VolumeCroppingControlTool, DragProbeTool, WindowLevelTool, ZoomTool, StackScrollTool, SegmentBidirectionalTool, PlanarRotateTool, MIPJumpToClickTool, LabelTool, LengthTool, HeightTool, ProbeTool, RectangleROITool, EllipticalROITool, CircleROITool, ETDRSGridTool, SplineROITool, SplineContourSegmentationTool, BidirectionalTool, PlanarFreehandROITool, PlanarFreehandContourSegmentationTool, LivewireContourTool, LivewireContourSegmentationTool, ArrowAnnotateTool, KeyImageTool, CrosshairsTool, WorldCrosshairTool, SliceIntersectionTool, ReferenceLinesTool, RectangleScissorsTool, CircleScissorsTool, SphereScissorsTool, RectangleROIThresholdTool, RectangleROIStartEndThresholdTool, CircleROIStartEndThresholdTool, BrushTool, AngleTool, CobbAngleTool, UltrasoundDirectionalTool, UltrasoundPleuraBLineTool, MagnifyTool, AdvancedMagnifyTool, ReferenceCursors, PaintFillTool, ScaleOverlayTool, OrientationMarkerTool, OrientationControllerTool, OverlayGridTool, SegmentationIntersectionTool, EraserTool, SculptorTool, SegmentSelectTool, WindowLevelRegionTool, VolumeRotateTool, RegionSegmentPlusTool, RegionSegmentTool, ClickSegmentTool, WholeBodySegmentTool, LabelmapBaseTool, SegmentLabelTool, LabelMapEditWithContourTool, } from './tools/index.js';
|
|
15
|
+
import { BaseTool, AnnotationTool, AnnotationDisplayTool, measurementTargetFilters, PanTool, TrackballRotateTool, VolumeCroppingTool, VolumeCroppingControlTool, DragProbeTool, WindowLevelTool, ZoomTool, StackScrollTool, SegmentBidirectionalTool, PlanarRotateTool, MIPJumpToClickTool, LabelTool, LengthTool, HeightTool, ProbeTool, RectangleROITool, EllipticalROITool, CircleROITool, ETDRSGridTool, SplineROITool, SplineContourSegmentationTool, BidirectionalTool, PlanarFreehandROITool, PlanarFreehandContourSegmentationTool, LivewireContourTool, LivewireContourSegmentationTool, ArrowAnnotateTool, KeyImageTool, CrosshairsTool, WorldCrosshairTool, SliceIntersectionTool, ReferenceLinesTool, RectangleScissorsTool, CircleScissorsTool, SphereScissorsTool, RectangleROIThresholdTool, RectangleROIStartEndThresholdTool, CircleROIStartEndThresholdTool, BrushTool, AngleTool, CobbAngleTool, UltrasoundDirectionalTool, UltrasoundPleuraBLineTool, MagnifyTool, AdvancedMagnifyTool, ReferenceCursors, PaintFillTool, ScaleOverlayTool, OrientationMarkerTool, OrientationControllerTool, OverlayGridTool, SegmentationIntersectionTool, EraserTool, SculptorTool, SegmentSelectTool, WindowLevelRegionTool, VolumeRotateTool, RegionSegmentPlusTool, RegionSegmentTool, ClickSegmentTool, WholeBodySegmentTool, LabelmapBaseTool, SegmentLabelTool, LabelMapEditWithContourTool, } from './tools/index.js';
|
|
16
16
|
import VideoRedactionTool from './tools/annotation/VideoRedactionTool.js';
|
|
17
17
|
import * as Enums from './enums/index.js';
|
|
18
|
-
export { VideoRedactionTool, init, destroy, addTool, removeTool, cancelActiveManipulations, BaseTool, AnnotationTool, AnnotationDisplayTool, PanTool, SegmentBidirectionalTool, TrackballRotateTool, VolumeCroppingTool, VolumeCroppingControlTool, DragProbeTool, WindowLevelTool, WindowLevelRegionTool, ZoomTool, StackScrollTool, PlanarRotateTool, MIPJumpToClickTool, LabelTool, LengthTool, HeightTool, CrosshairsTool, WorldCrosshairTool, SliceIntersectionTool, ReferenceLinesTool, OverlayGridTool, SegmentationIntersectionTool, ProbeTool, RectangleROITool, EllipticalROITool, CircleROITool, ETDRSGridTool, SplineROITool, SplineContourSegmentationTool, BidirectionalTool, PlanarFreehandROITool, PlanarFreehandContourSegmentationTool, LivewireContourTool, LivewireContourSegmentationTool, ArrowAnnotateTool, AngleTool, CobbAngleTool, UltrasoundDirectionalTool, UltrasoundPleuraBLineTool, KeyImageTool, MagnifyTool, AdvancedMagnifyTool, ReferenceCursors, ScaleOverlayTool, SculptorTool, EraserTool, RectangleScissorsTool, CircleScissorsTool, SphereScissorsTool, RectangleROIThresholdTool, RectangleROIStartEndThresholdTool, CircleROIStartEndThresholdTool, BrushTool, OrientationMarkerTool, OrientationControllerTool, SegmentSelectTool, SegmentLabelTool, synchronizers, Synchronizer, SynchronizerManager, PaintFillTool, Types, state, ToolGroupManager, store, Enums, CONSTANTS, drawing, annotation, segmentation, utilities, cursors, VolumeRotateTool, RegionSegmentPlusTool, RegionSegmentTool, ClickSegmentTool, WholeBodySegmentTool, LabelmapBaseTool, LabelMapEditWithContourTool, splines, version, };
|
|
18
|
+
export { VideoRedactionTool, init, destroy, addTool, removeTool, cancelActiveManipulations, BaseTool, AnnotationTool, AnnotationDisplayTool, measurementTargetFilters, PanTool, SegmentBidirectionalTool, TrackballRotateTool, VolumeCroppingTool, VolumeCroppingControlTool, DragProbeTool, WindowLevelTool, WindowLevelRegionTool, ZoomTool, StackScrollTool, PlanarRotateTool, MIPJumpToClickTool, LabelTool, LengthTool, HeightTool, CrosshairsTool, WorldCrosshairTool, SliceIntersectionTool, ReferenceLinesTool, OverlayGridTool, SegmentationIntersectionTool, ProbeTool, RectangleROITool, EllipticalROITool, CircleROITool, ETDRSGridTool, SplineROITool, SplineContourSegmentationTool, BidirectionalTool, PlanarFreehandROITool, PlanarFreehandContourSegmentationTool, LivewireContourTool, LivewireContourSegmentationTool, ArrowAnnotateTool, AngleTool, CobbAngleTool, UltrasoundDirectionalTool, UltrasoundPleuraBLineTool, KeyImageTool, MagnifyTool, AdvancedMagnifyTool, ReferenceCursors, ScaleOverlayTool, SculptorTool, EraserTool, RectangleScissorsTool, CircleScissorsTool, SphereScissorsTool, RectangleROIThresholdTool, RectangleROIStartEndThresholdTool, CircleROIStartEndThresholdTool, BrushTool, OrientationMarkerTool, OrientationControllerTool, SegmentSelectTool, SegmentLabelTool, synchronizers, Synchronizer, SynchronizerManager, PaintFillTool, Types, state, ToolGroupManager, store, Enums, CONSTANTS, drawing, annotation, segmentation, utilities, cursors, VolumeRotateTool, RegionSegmentPlusTool, RegionSegmentTool, ClickSegmentTool, WholeBodySegmentTool, LabelmapBaseTool, LabelMapEditWithContourTool, splines, version, };
|
|
@@ -16,7 +16,6 @@ import * as lineSegment from '../../utilities/math/line/index.js';
|
|
|
16
16
|
import { resetElementCursor, hideElementCursor, } from '../../cursors/elementCursor.js';
|
|
17
17
|
import triggerAnnotationRenderForViewportIds from '../../utilities/triggerAnnotationRenderForViewportIds.js';
|
|
18
18
|
import { getStyleProperty } from '../../stateManagement/annotation/config/helpers.js';
|
|
19
|
-
const { transformWorldToIndex } = csUtils;
|
|
20
19
|
class BidirectionalTool extends AnnotationTool {
|
|
21
20
|
constructor(toolProps = {}, defaultToolProps = {
|
|
22
21
|
supportedInteractionTypes: ['Mouse', 'Touch'],
|
|
@@ -2,6 +2,7 @@ import { AnnotationTool } from '../base/index.js';
|
|
|
2
2
|
import type { Types } from '@cornerstonejs/core';
|
|
3
3
|
import type { EventTypes, ToolHandle, PublicToolProps, ToolProps, SVGDrawingHelper, Annotation } from '../../types/index.js';
|
|
4
4
|
import type { CircleROIAnnotation } from '../../types/ToolSpecificAnnotationTypes.js';
|
|
5
|
+
import { type MetricDefinition } from '../../utilities/defaultGetTextLines.js';
|
|
5
6
|
declare class CircleROITool extends AnnotationTool {
|
|
6
7
|
static toolName: string;
|
|
7
8
|
_throttledCalculateCachedStats: Function;
|
|
@@ -16,7 +17,7 @@ declare class CircleROITool extends AnnotationTool {
|
|
|
16
17
|
isDrawing: boolean;
|
|
17
18
|
isHandleOutsideImage: boolean;
|
|
18
19
|
constructor(toolProps?: PublicToolProps, defaultToolProps?: ToolProps);
|
|
19
|
-
addNewAnnotation: (evt: EventTypes.InteractionEventType) =>
|
|
20
|
+
addNewAnnotation: (evt: EventTypes.InteractionEventType) => Annotation;
|
|
20
21
|
isPointNearTool: (element: HTMLDivElement, annotation: CircleROIAnnotation, canvasCoords: Types.Point2, proximity: number) => boolean;
|
|
21
22
|
_pointInEllipseCanvas(ellipse: any, location: Types.Point2): boolean;
|
|
22
23
|
toolSelectedCallback: (evt: EventTypes.InteractionEventType, annotation: CircleROIAnnotation) => void;
|
|
@@ -39,5 +40,7 @@ declare class CircleROITool extends AnnotationTool {
|
|
|
39
40
|
viewplaneNormal?: Types.Point3;
|
|
40
41
|
viewUp?: Types.Point3;
|
|
41
42
|
}) => CircleROIAnnotation;
|
|
43
|
+
static readonly CIRCLE_ROI_METRICS: MetricDefinition[];
|
|
44
|
+
static getTextLines: (data: any, targetId: string | string[]) => string[];
|
|
42
45
|
}
|
|
43
46
|
export default CircleROITool;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
var _a;
|
|
2
|
-
import { AnnotationTool, BaseTool } from '../base/index.js';
|
|
2
|
+
import { AnnotationTool, BaseTool, measurementTargetFilters } from '../base/index.js';
|
|
3
3
|
import { vec2, vec3 } from 'gl-matrix';
|
|
4
4
|
import { getEnabledElement, VolumeViewport, utilities as csUtils, getEnabledElementByViewportId, EPSILON, } from '@cornerstonejs/core';
|
|
5
5
|
import { getCalibratedLengthUnitsAndScale } from '../../utilities/getCalibratedUnits.js';
|
|
@@ -21,6 +21,7 @@ import { getCanvasCircleCorners, getCanvasCircleRadius, } from '../../utilities/
|
|
|
21
21
|
import { getCanvasEllipseCorners, pointInEllipse, } from '../../utilities/math/ellipse/index.js';
|
|
22
22
|
import { BasicStatsCalculator } from '../../utilities/math/basic/index.js';
|
|
23
23
|
import { getStyleProperty } from '../../stateManagement/annotation/config/helpers.js';
|
|
24
|
+
import { createGetTextLines, } from '../../utilities/defaultGetTextLines.js';
|
|
24
25
|
import getEllipseWorldCoordinates from '../../utilities/getEllipseWorldCoordinates.js';
|
|
25
26
|
const { transformWorldToIndex } = csUtils;
|
|
26
27
|
class CircleROITool extends AnnotationTool {
|
|
@@ -35,6 +36,7 @@ class CircleROITool extends AnnotationTool {
|
|
|
35
36
|
getTextLines: defaultGetTextLines,
|
|
36
37
|
statsCalculator: BasicStatsCalculator,
|
|
37
38
|
simplified: true,
|
|
39
|
+
targetsFilter: measurementTargetFilters.allPixelData,
|
|
38
40
|
},
|
|
39
41
|
}) {
|
|
40
42
|
super(toolProps, defaultToolProps);
|
|
@@ -360,7 +362,7 @@ class CircleROITool extends AnnotationTool {
|
|
|
360
362
|
const { annotationUID, data } = annotation;
|
|
361
363
|
const { handles } = data;
|
|
362
364
|
const { points, activeHandleIndex } = handles;
|
|
363
|
-
const
|
|
365
|
+
const targetIds = this.getMeasurementTargets(viewport, data);
|
|
364
366
|
styleSpecifier.annotationUID = annotationUID;
|
|
365
367
|
const { color, lineWidth, lineDash } = this.getAnnotationStyle({
|
|
366
368
|
annotation,
|
|
@@ -374,19 +376,7 @@ class CircleROITool extends AnnotationTool {
|
|
|
374
376
|
canvasCoordinates[1],
|
|
375
377
|
]);
|
|
376
378
|
const { centerPointRadius } = this.configuration;
|
|
377
|
-
if (
|
|
378
|
-
data.cachedStats[targetId].areaUnit == null) {
|
|
379
|
-
data.cachedStats[targetId] = {
|
|
380
|
-
Modality: null,
|
|
381
|
-
area: null,
|
|
382
|
-
max: null,
|
|
383
|
-
mean: null,
|
|
384
|
-
stdDev: null,
|
|
385
|
-
areaUnit: null,
|
|
386
|
-
radius: null,
|
|
387
|
-
radiusUnit: null,
|
|
388
|
-
perimeter: null,
|
|
389
|
-
};
|
|
379
|
+
if (this.ensureCachedStatsTargets(data, targetIds, (stats) => stats.areaUnit == null)) {
|
|
390
380
|
this._calculateCachedStats(annotation, viewport, renderingEngine, enabledElement);
|
|
391
381
|
}
|
|
392
382
|
else if (annotation.invalidated) {
|
|
@@ -456,7 +446,7 @@ class CircleROITool extends AnnotationTool {
|
|
|
456
446
|
}
|
|
457
447
|
renderStatus = true;
|
|
458
448
|
if (this.configuration.calculateStats) {
|
|
459
|
-
const textLines = this.configuration.getTextLines(data,
|
|
449
|
+
const textLines = this.configuration.getTextLines(data, targetIds);
|
|
460
450
|
if (!textLines || textLines.length === 0) {
|
|
461
451
|
continue;
|
|
462
452
|
}
|
|
@@ -491,13 +481,19 @@ class CircleROITool extends AnnotationTool {
|
|
|
491
481
|
const topLeftWorld = viewport.canvasToWorld(topLeftCanvas);
|
|
492
482
|
const bottomRightWorld = viewport.canvasToWorld(bottomRightCanvas);
|
|
493
483
|
const { cachedStats } = data;
|
|
484
|
+
if (wasInvalidated) {
|
|
485
|
+
const currentTargets = this.getMeasurementTargets(viewport, data);
|
|
486
|
+
for (const key of Object.keys(cachedStats)) {
|
|
487
|
+
delete cachedStats[key];
|
|
488
|
+
}
|
|
489
|
+
this.ensureCachedStatsTargets(data, currentTargets);
|
|
490
|
+
}
|
|
494
491
|
const targetIds = Object.keys(cachedStats);
|
|
492
|
+
let isHandleOutsideAnyTarget = false;
|
|
495
493
|
for (let i = 0; i < targetIds.length; i++) {
|
|
496
494
|
const targetId = targetIds[i];
|
|
497
495
|
const image = this.getTargetImageData(targetId);
|
|
498
496
|
if (!image) {
|
|
499
|
-
console.warn('image not found for stats:', targetId);
|
|
500
|
-
delete cachedStats[targetId];
|
|
501
497
|
continue;
|
|
502
498
|
}
|
|
503
499
|
const { dimensions, imageData, metadata, voxelManager } = image;
|
|
@@ -539,11 +535,12 @@ class CircleROITool extends AnnotationTool {
|
|
|
539
535
|
};
|
|
540
536
|
const pos1Index = transformWorldToIndex(imageData, topLeftWorld);
|
|
541
537
|
const pos2Index = transformWorldToIndex(imageData, bottomRightWorld);
|
|
542
|
-
|
|
538
|
+
const isHandleOutsideTarget = !BaseTool.isInsideVolume(dimensions, [
|
|
543
539
|
pos1Index,
|
|
544
540
|
pos2Index,
|
|
545
541
|
]);
|
|
546
|
-
|
|
542
|
+
isHandleOutsideAnyTarget ||= isHandleOutsideTarget;
|
|
543
|
+
if (!isHandleOutsideTarget) {
|
|
547
544
|
const iMin = Math.min(pos1Index[0], pos2Index[0]);
|
|
548
545
|
const iMax = Math.max(pos1Index[0], pos2Index[0]);
|
|
549
546
|
const jMin = Math.min(pos1Index[1], pos2Index[1]);
|
|
@@ -593,6 +590,7 @@ class CircleROITool extends AnnotationTool {
|
|
|
593
590
|
};
|
|
594
591
|
}
|
|
595
592
|
}
|
|
593
|
+
this.isHandleOutsideImage = isHandleOutsideAnyTarget;
|
|
596
594
|
annotation.invalidated = false;
|
|
597
595
|
if (wasInvalidated) {
|
|
598
596
|
triggerAnnotationModified(annotation, element, ChangeTypes.StatsUpdated);
|
|
@@ -660,10 +658,20 @@ CircleROITool.hydrate = (viewportId, points, options) => {
|
|
|
660
658
|
addAnnotation(annotation, viewport.element);
|
|
661
659
|
triggerAnnotationRenderForViewportIds([viewport.id]);
|
|
662
660
|
};
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
661
|
+
CircleROITool.CIRCLE_ROI_METRICS = [
|
|
662
|
+
{ name: 'Mean', attribute: 'mean', unitAttribute: 'modalityUnit' },
|
|
663
|
+
{ name: 'Max', attribute: 'max', unitAttribute: 'modalityUnit' },
|
|
664
|
+
{ name: 'Min', attribute: 'min', unitAttribute: 'modalityUnit' },
|
|
665
|
+
{ name: 'Std Dev', attribute: 'stdDev', unitAttribute: 'modalityUnit' },
|
|
666
|
+
];
|
|
667
|
+
CircleROITool.getTextLines = createGetTextLines(_a.CIRCLE_ROI_METRICS);
|
|
668
|
+
function defaultGetTextLines(data, targetIds) {
|
|
669
|
+
const cachedVolumeStats = data.cachedStats[targetIds[0]];
|
|
666
670
|
const textLines = [];
|
|
671
|
+
if (!cachedVolumeStats) {
|
|
672
|
+
return textLines;
|
|
673
|
+
}
|
|
674
|
+
const { radius, radiusUnit, area, isEmptyArea, areaUnit } = cachedVolumeStats;
|
|
667
675
|
if (csUtils.isNumber(radius)) {
|
|
668
676
|
const radiusLine = isEmptyArea
|
|
669
677
|
? `Radius: Oblique not supported`
|
|
@@ -676,17 +684,9 @@ function defaultGetTextLines(data, targetId) {
|
|
|
676
684
|
: `Area: ${csUtils.roundNumber(area)} ${areaUnit}`;
|
|
677
685
|
textLines.push(areaLine);
|
|
678
686
|
}
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
if (csUtils.isNumber(max)) {
|
|
683
|
-
textLines.push(`Max: ${csUtils.roundNumber(max)} ${modalityUnit}`);
|
|
684
|
-
}
|
|
685
|
-
if (csUtils.isNumber(min)) {
|
|
686
|
-
textLines.push(`Min: ${csUtils.roundNumber(min)} ${modalityUnit}`);
|
|
687
|
-
}
|
|
688
|
-
if (csUtils.isNumber(stdDev)) {
|
|
689
|
-
textLines.push(`Std Dev: ${csUtils.roundNumber(stdDev)} ${modalityUnit}`);
|
|
687
|
+
const standardTextLines = CircleROITool.getTextLines(data, targetIds);
|
|
688
|
+
if (standardTextLines) {
|
|
689
|
+
textLines.push(...standardTextLines);
|
|
690
690
|
}
|
|
691
691
|
return textLines;
|
|
692
692
|
}
|
|
@@ -20,7 +20,7 @@ class DragProbeTool extends ProbeTool {
|
|
|
20
20
|
const { currentPoints, element } = eventDetail;
|
|
21
21
|
const worldPos = currentPoints.world;
|
|
22
22
|
const enabledElement = getEnabledElement(element);
|
|
23
|
-
const { viewport
|
|
23
|
+
const { viewport } = enabledElement;
|
|
24
24
|
this.isDrawing = true;
|
|
25
25
|
const camera = getViewportICamera(viewport);
|
|
26
26
|
const { viewPlaneNormal, viewUp } = camera;
|
|
@@ -16,7 +16,7 @@ declare class RectangleROITool extends AnnotationTool {
|
|
|
16
16
|
isDrawing: boolean;
|
|
17
17
|
isHandleOutsideImage: boolean;
|
|
18
18
|
constructor(toolProps?: PublicToolProps, defaultToolProps?: ToolProps);
|
|
19
|
-
addNewAnnotation: (evt: EventTypes.InteractionEventType) =>
|
|
19
|
+
addNewAnnotation: (evt: EventTypes.InteractionEventType) => Annotation;
|
|
20
20
|
isPointNearTool: (element: HTMLDivElement, annotation: RectangleROIAnnotation, canvasCoords: Types.Point2, proximity: number) => boolean;
|
|
21
21
|
toolSelectedCallback: (evt: EventTypes.InteractionEventType, annotation: RectangleROIAnnotation) => void;
|
|
22
22
|
handleSelectedCallback: (evt: EventTypes.InteractionEventType, annotation: RectangleROIAnnotation, handle: ToolHandle) => void;
|
|
@@ -34,7 +34,7 @@ declare class RectangleROITool extends AnnotationTool {
|
|
|
34
34
|
width: number;
|
|
35
35
|
height: number;
|
|
36
36
|
};
|
|
37
|
-
_calculateCachedStats: (annotation: any, viewPlaneNormal: any, viewUp: any,
|
|
37
|
+
_calculateCachedStats: (annotation: any, viewPlaneNormal: any, viewUp: any, enabledElement: any) => any;
|
|
38
38
|
_isInsideVolume: (index1: any, index2: any, dimensions: any) => boolean;
|
|
39
39
|
static hydrate: (viewportId: string, points: Types.Point3[], options?: {
|
|
40
40
|
annotationUID?: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
var _a;
|
|
2
|
-
import { AnnotationTool } from '../base/index.js';
|
|
2
|
+
import { AnnotationTool, measurementTargetFilters } from '../base/index.js';
|
|
3
3
|
import { getEnabledElement, VolumeViewport, utilities as csUtils, getEnabledElementByViewportId, } from '@cornerstonejs/core';
|
|
4
4
|
import { vec3 } from 'gl-matrix';
|
|
5
5
|
import { getCalibratedLengthUnitsAndScale } from '../../utilities/getCalibratedUnits.js';
|
|
@@ -21,6 +21,7 @@ import { viewportSupportsImageSlices } from '../../utilities/viewportCapabilitie
|
|
|
21
21
|
import { isViewportPreScaled } from '../../utilities/viewport/isViewportPreScaled.js';
|
|
22
22
|
import { BasicStatsCalculator } from '../../utilities/math/basic/index.js';
|
|
23
23
|
import { getStyleProperty } from '../../stateManagement/annotation/config/helpers.js';
|
|
24
|
+
import { defaultAreaGetTextLines } from '../../utilities/defaultGetTextLines.js';
|
|
24
25
|
const { transformWorldToIndex, transformWorldToIndexContinuous } = csUtils;
|
|
25
26
|
class RectangleROITool extends AnnotationTool {
|
|
26
27
|
constructor(toolProps = {}, defaultToolProps = {
|
|
@@ -30,8 +31,9 @@ class RectangleROITool extends AnnotationTool {
|
|
|
30
31
|
shadow: true,
|
|
31
32
|
preventHandleOutsideImage: false,
|
|
32
33
|
calculateStats: true,
|
|
33
|
-
getTextLines:
|
|
34
|
+
getTextLines: defaultAreaGetTextLines,
|
|
34
35
|
statsCalculator: BasicStatsCalculator,
|
|
36
|
+
targetsFilter: measurementTargetFilters.allPixelData,
|
|
35
37
|
},
|
|
36
38
|
}) {
|
|
37
39
|
super(toolProps, defaultToolProps);
|
|
@@ -112,8 +114,6 @@ class RectangleROITool extends AnnotationTool {
|
|
|
112
114
|
};
|
|
113
115
|
this._activateModify(element);
|
|
114
116
|
hideElementCursor(element);
|
|
115
|
-
const enabledElement = getEnabledElement(element);
|
|
116
|
-
const { renderingEngine } = enabledElement;
|
|
117
117
|
triggerAnnotationRenderForViewportIds(viewportIdsToRender);
|
|
118
118
|
evt.preventDefault();
|
|
119
119
|
};
|
|
@@ -139,8 +139,6 @@ class RectangleROITool extends AnnotationTool {
|
|
|
139
139
|
};
|
|
140
140
|
this._activateModify(element);
|
|
141
141
|
hideElementCursor(element);
|
|
142
|
-
const enabledElement = getEnabledElement(element);
|
|
143
|
-
const { renderingEngine } = enabledElement;
|
|
144
142
|
triggerAnnotationRenderForViewportIds(viewportIdsToRender);
|
|
145
143
|
evt.preventDefault();
|
|
146
144
|
};
|
|
@@ -329,27 +327,18 @@ class RectangleROITool extends AnnotationTool {
|
|
|
329
327
|
const { annotationUID, data } = annotation;
|
|
330
328
|
const { points, activeHandleIndex } = data.handles;
|
|
331
329
|
const canvasCoordinates = points.map((p) => viewport.worldToCanvas(p));
|
|
332
|
-
const
|
|
330
|
+
const targetIds = this.getMeasurementTargets(viewport, data);
|
|
333
331
|
styleSpecifier.annotationUID = annotationUID;
|
|
334
332
|
const { color, lineWidth, lineDash } = this.getAnnotationStyle({
|
|
335
333
|
annotation,
|
|
336
334
|
styleSpecifier,
|
|
337
335
|
});
|
|
338
336
|
const { viewPlaneNormal, viewUp } = getViewportICamera(viewport);
|
|
339
|
-
if (
|
|
340
|
-
|
|
341
|
-
data.cachedStats[targetId] = {
|
|
342
|
-
Modality: null,
|
|
343
|
-
area: null,
|
|
344
|
-
max: null,
|
|
345
|
-
mean: null,
|
|
346
|
-
stdDev: null,
|
|
347
|
-
areaUnit: null,
|
|
348
|
-
};
|
|
349
|
-
this._calculateCachedStats(annotation, viewPlaneNormal, viewUp, renderingEngine, enabledElement);
|
|
337
|
+
if (this.ensureCachedStatsTargets(data, targetIds, (stats) => stats.areaUnit == null)) {
|
|
338
|
+
this._calculateCachedStats(annotation, viewPlaneNormal, viewUp, enabledElement);
|
|
350
339
|
}
|
|
351
340
|
else if (annotation.invalidated) {
|
|
352
|
-
this._throttledCalculateCachedStats(annotation, viewPlaneNormal, viewUp,
|
|
341
|
+
this._throttledCalculateCachedStats(annotation, viewPlaneNormal, viewUp, enabledElement);
|
|
353
342
|
if (viewport instanceof VolumeViewport) {
|
|
354
343
|
const { referencedImageId } = annotation.metadata;
|
|
355
344
|
for (const targetId in data.cachedStats) {
|
|
@@ -399,8 +388,8 @@ class RectangleROITool extends AnnotationTool {
|
|
|
399
388
|
lineWidth,
|
|
400
389
|
}, dataId);
|
|
401
390
|
renderStatus = true;
|
|
402
|
-
const textLines = this.configuration.getTextLines(data,
|
|
403
|
-
if (!textLines
|
|
391
|
+
const textLines = this.configuration.getTextLines(data, targetIds);
|
|
392
|
+
if (!textLines?.length) {
|
|
404
393
|
continue;
|
|
405
394
|
}
|
|
406
395
|
if (!this.renderLinkedTextBoxAnnotation({
|
|
@@ -425,7 +414,7 @@ class RectangleROITool extends AnnotationTool {
|
|
|
425
414
|
height: Math.abs(point0[1] - point1[1]),
|
|
426
415
|
};
|
|
427
416
|
};
|
|
428
|
-
this._calculateCachedStats = (annotation, viewPlaneNormal, viewUp,
|
|
417
|
+
this._calculateCachedStats = (annotation, viewPlaneNormal, viewUp, enabledElement) => {
|
|
429
418
|
if (!this.configuration.calculateStats) {
|
|
430
419
|
return;
|
|
431
420
|
}
|
|
@@ -434,7 +423,15 @@ class RectangleROITool extends AnnotationTool {
|
|
|
434
423
|
const { element } = viewport;
|
|
435
424
|
const worldHandles = data.handles.points;
|
|
436
425
|
const { cachedStats } = data;
|
|
426
|
+
if (annotation.invalidated) {
|
|
427
|
+
const currentTargets = this.getMeasurementTargets(viewport, data);
|
|
428
|
+
for (const key of Object.keys(cachedStats)) {
|
|
429
|
+
delete cachedStats[key];
|
|
430
|
+
}
|
|
431
|
+
this.ensureCachedStatsTargets(data, currentTargets);
|
|
432
|
+
}
|
|
437
433
|
const targetIds = Object.keys(cachedStats);
|
|
434
|
+
let isHandleOutsideAnyTarget = false;
|
|
438
435
|
for (let i = 0; i < targetIds.length; i++) {
|
|
439
436
|
const targetId = targetIds[i];
|
|
440
437
|
const image = this.getTargetImageData(targetId);
|
|
@@ -445,8 +442,9 @@ class RectangleROITool extends AnnotationTool {
|
|
|
445
442
|
const continuousIndexHandles = worldHandles.map((worldHandle) => transformWorldToIndexContinuous(imageData, worldHandle));
|
|
446
443
|
const pos1Index = transformWorldToIndex(imageData, worldHandles[0]);
|
|
447
444
|
const pos2Index = transformWorldToIndex(imageData, worldHandles[3]);
|
|
448
|
-
|
|
449
|
-
|
|
445
|
+
const isHandleOutsideTarget = !this._isInsideVolume(pos1Index, pos2Index, dimensions);
|
|
446
|
+
isHandleOutsideAnyTarget ||= isHandleOutsideTarget;
|
|
447
|
+
if (!isHandleOutsideTarget) {
|
|
450
448
|
const iMin = Math.min(pos1Index[0], pos2Index[0]);
|
|
451
449
|
const iMax = Math.max(pos1Index[0], pos2Index[0]);
|
|
452
450
|
const jMin = Math.min(pos1Index[1], pos2Index[1]);
|
|
@@ -498,12 +496,12 @@ class RectangleROITool extends AnnotationTool {
|
|
|
498
496
|
};
|
|
499
497
|
}
|
|
500
498
|
else {
|
|
501
|
-
this.isHandleOutsideImage = true;
|
|
502
499
|
cachedStats[targetId] = {
|
|
503
500
|
Modality: metadata.Modality,
|
|
504
501
|
};
|
|
505
502
|
}
|
|
506
503
|
}
|
|
504
|
+
this.isHandleOutsideImage = isHandleOutsideAnyTarget;
|
|
507
505
|
const invalidated = annotation.invalidated;
|
|
508
506
|
annotation.invalidated = false;
|
|
509
507
|
if (invalidated) {
|
|
@@ -553,28 +551,4 @@ RectangleROITool.hydrate = (viewportId, points, options) => {
|
|
|
553
551
|
addAnnotation(annotation, viewport.element);
|
|
554
552
|
triggerAnnotationRenderForViewportIds([viewport.id]);
|
|
555
553
|
};
|
|
556
|
-
function defaultGetTextLines(data, targetId) {
|
|
557
|
-
const cachedVolumeStats = data.cachedStats[targetId];
|
|
558
|
-
const { area, mean, max, stdDev, areaUnit, modalityUnit, min } = cachedVolumeStats;
|
|
559
|
-
if (mean === undefined || mean === null) {
|
|
560
|
-
return;
|
|
561
|
-
}
|
|
562
|
-
const textLines = [];
|
|
563
|
-
if (csUtils.isNumber(area)) {
|
|
564
|
-
textLines.push(`Area: ${csUtils.roundNumber(area)} ${areaUnit}`);
|
|
565
|
-
}
|
|
566
|
-
if (csUtils.isNumber(mean)) {
|
|
567
|
-
textLines.push(`Mean: ${csUtils.roundNumber(mean)} ${modalityUnit}`);
|
|
568
|
-
}
|
|
569
|
-
if (csUtils.isNumber(max)) {
|
|
570
|
-
textLines.push(`Max: ${csUtils.roundNumber(max)} ${modalityUnit}`);
|
|
571
|
-
}
|
|
572
|
-
if (csUtils.isNumber(min)) {
|
|
573
|
-
textLines.push(`Min: ${csUtils.roundNumber(min)} ${modalityUnit}`);
|
|
574
|
-
}
|
|
575
|
-
if (csUtils.isNumber(stdDev)) {
|
|
576
|
-
textLines.push(`Std Dev: ${csUtils.roundNumber(stdDev)} ${modalityUnit}`);
|
|
577
|
-
}
|
|
578
|
-
return textLines;
|
|
579
|
-
}
|
|
580
554
|
export default RectangleROITool;
|
|
@@ -17,7 +17,9 @@ import getViewportICamera from '../../utilities/getViewportICamera.js';
|
|
|
17
17
|
const { DefaultHistoryMemo } = csUtils.HistoryMemo;
|
|
18
18
|
class AnnotationTool extends AnnotationDisplayTool {
|
|
19
19
|
static createAnnotationForViewport(viewport, ...annotationBaseData) {
|
|
20
|
-
|
|
20
|
+
const metadata = viewport.getViewReference();
|
|
21
|
+
delete metadata.volumeId;
|
|
22
|
+
return this.createAnnotation({ metadata }, ...annotationBaseData);
|
|
21
23
|
}
|
|
22
24
|
static createAndAddAnnotation(viewport, ...annotationBaseData) {
|
|
23
25
|
const annotation = this.createAnnotationForViewport(viewport, ...annotationBaseData);
|
|
@@ -184,9 +186,9 @@ class AnnotationTool extends AnnotationDisplayTool {
|
|
|
184
186
|
return true;
|
|
185
187
|
}
|
|
186
188
|
static isSuvScaled(viewport, targetId, imageId) {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
189
|
+
const volumeId = csUtils.getVolumeId(targetId);
|
|
190
|
+
const volume = cache.getVolume(volumeId);
|
|
191
|
+
if (volume) {
|
|
190
192
|
return volume?.scaling?.PT !== undefined;
|
|
191
193
|
}
|
|
192
194
|
const scalingModule = imageId && metaData.get('scalingModule', imageId);
|
|
@@ -2,7 +2,7 @@ import { utilities as csUtils } from '@cornerstonejs/core';
|
|
|
2
2
|
import type { Types } from '@cornerstonejs/core';
|
|
3
3
|
import ToolModes from '../../enums/ToolModes.js';
|
|
4
4
|
import type StrategyCallbacks from '../../enums/StrategyCallbacks.js';
|
|
5
|
-
import type { InteractionTypes, ToolProps, PublicToolProps, ToolConfiguration } from '../../types/index.js';
|
|
5
|
+
import type { InteractionTypes, ToolProps, PublicToolProps, ToolConfiguration, AnnotationData, MeasurementTargetCandidate } from '../../types/index.js';
|
|
6
6
|
declare abstract class BaseTool {
|
|
7
7
|
static toolName: any;
|
|
8
8
|
static activeCursorTool: any;
|
|
@@ -33,9 +33,20 @@ declare abstract class BaseTool {
|
|
|
33
33
|
setConfiguration(newConfiguration: Record<string, any>): void;
|
|
34
34
|
setActiveStrategy(strategyName: string): void;
|
|
35
35
|
protected getTargetImageData(targetId: string): Types.IImageData | Types.CPUIImageData;
|
|
36
|
-
protected getTargetId(viewport: Types.IViewport, data?:
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
protected getTargetId(viewport: Types.IViewport, data?: AnnotationData): string | undefined;
|
|
37
|
+
protected getMeasurementTargets(viewport: Types.IViewport, data?: AnnotationData): string[];
|
|
38
|
+
protected getMeasurementTargetCandidates(viewport: Types.IViewport, data?: AnnotationData): MeasurementTargetCandidate[];
|
|
39
|
+
protected static getViewportDisplaySets(viewport: Types.IViewport): Array<{
|
|
40
|
+
displaySetUID: string;
|
|
41
|
+
displaySet?: unknown;
|
|
42
|
+
instance?: Record<string, unknown>;
|
|
43
|
+
imageIds?: string[];
|
|
44
|
+
volumeId?: string;
|
|
45
|
+
modality?: string;
|
|
46
|
+
}>;
|
|
47
|
+
protected static getExemplarInstance(imageIds?: string[]): Record<string, unknown> | undefined;
|
|
48
|
+
protected static findCachedStatsTargetId(data: AnnotationData | undefined, referencedId: string): string | undefined;
|
|
49
|
+
protected ensureCachedStatsTargets(data: AnnotationData, targetIds: string[], needsUpdate?: (stats: any) => boolean): boolean;
|
|
39
50
|
undo(): void;
|
|
40
51
|
redo(): void;
|
|
41
52
|
static createZoomPanMemo(viewport: any): {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { utilities as csUtils, viewportHasPan, viewportHasZoom, } from '@cornerstonejs/core';
|
|
1
|
+
import { cache, metaData, utilities as csUtils, viewportHasPan, viewportHasZoom, } from '@cornerstonejs/core';
|
|
2
2
|
import ToolModes from '../../enums/ToolModes.js';
|
|
3
|
+
import measurementTargetFilters from './measurementTargetFilters.js';
|
|
3
4
|
const { DefaultHistoryMemo } = csUtils.HistoryMemo;
|
|
4
5
|
class BaseTool {
|
|
5
6
|
get configurationTyped() {
|
|
@@ -71,7 +72,7 @@ class BaseTool {
|
|
|
71
72
|
if (!viewports || !viewports.length) {
|
|
72
73
|
return;
|
|
73
74
|
}
|
|
74
|
-
return viewports[0].getImageData();
|
|
75
|
+
return viewports[0].getImageData(volumeId);
|
|
75
76
|
}
|
|
76
77
|
else if (targetId.startsWith('videoId:')) {
|
|
77
78
|
const imageURI = csUtils.imageIdToURI(targetId);
|
|
@@ -86,19 +87,139 @@ class BaseTool {
|
|
|
86
87
|
}
|
|
87
88
|
}
|
|
88
89
|
getTargetId(viewport, data) {
|
|
89
|
-
|
|
90
|
+
return this.getMeasurementTargets(viewport, data)[0];
|
|
91
|
+
}
|
|
92
|
+
getMeasurementTargets(viewport, data) {
|
|
93
|
+
const { targetsFilter, isPreferredTargetId } = this.configurationTyped;
|
|
90
94
|
if (isPreferredTargetId && data?.cachedStats) {
|
|
91
95
|
for (const [targetId, cachedStat] of Object.entries(data.cachedStats)) {
|
|
92
96
|
if (isPreferredTargetId(viewport, { targetId, cachedStat })) {
|
|
93
|
-
return targetId;
|
|
97
|
+
return [targetId];
|
|
94
98
|
}
|
|
95
99
|
}
|
|
96
100
|
}
|
|
101
|
+
if (targetsFilter) {
|
|
102
|
+
const candidates = this.getMeasurementTargetCandidates(viewport, data);
|
|
103
|
+
const options = {
|
|
104
|
+
viewport,
|
|
105
|
+
configuration: this.configurationTyped,
|
|
106
|
+
data,
|
|
107
|
+
};
|
|
108
|
+
return targetsFilter(candidates, options).map((candidate) => candidate.targetId);
|
|
109
|
+
}
|
|
97
110
|
const defaultTargetId = viewport.getViewReferenceId?.();
|
|
98
111
|
if (defaultTargetId) {
|
|
99
|
-
return defaultTargetId;
|
|
112
|
+
return [defaultTargetId];
|
|
113
|
+
}
|
|
114
|
+
throw new Error('getMeasurementTargets: viewport must have a getViewReferenceId method');
|
|
115
|
+
}
|
|
116
|
+
getMeasurementTargetCandidates(viewport, data) {
|
|
117
|
+
const candidates = [];
|
|
118
|
+
const displaySets = BaseTool.getViewportDisplaySets(viewport);
|
|
119
|
+
const actors = viewport.getActors?.() || [];
|
|
120
|
+
for (let index = 0; index < actors.length; index++) {
|
|
121
|
+
const { referencedId, representationUID } = actors[index];
|
|
122
|
+
if (!referencedId) {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
const volume = cache.getVolume(referencedId);
|
|
126
|
+
if (!volume) {
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
const targetId = BaseTool.findCachedStatsTargetId(data, referencedId) ||
|
|
130
|
+
viewport.getViewReferenceId?.({ volumeId: referencedId });
|
|
131
|
+
if (!targetId) {
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
const displaySetInfo = displaySets.find((displaySet) => displaySet.volumeId === referencedId);
|
|
135
|
+
const imageIds = displaySetInfo?.imageIds ??
|
|
136
|
+
(volume.imageIds?.length ? volume.imageIds : undefined);
|
|
137
|
+
const instance = displaySetInfo?.instance ?? BaseTool.getExemplarInstance(imageIds);
|
|
138
|
+
candidates.push({
|
|
139
|
+
targetId,
|
|
140
|
+
referencedId,
|
|
141
|
+
representationUID: representationUID,
|
|
142
|
+
displaySet: displaySetInfo?.displaySet,
|
|
143
|
+
displaySetUID: displaySetInfo?.displaySetUID,
|
|
144
|
+
instance,
|
|
145
|
+
modality: instance?.Modality ?? volume.metadata?.Modality,
|
|
146
|
+
imageIds,
|
|
147
|
+
index,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
if (!candidates.length) {
|
|
151
|
+
const targetId = viewport.getViewReferenceId?.();
|
|
152
|
+
if (targetId) {
|
|
153
|
+
const displaySetInfo = displaySets[0];
|
|
154
|
+
candidates.push({
|
|
155
|
+
targetId,
|
|
156
|
+
index: 0,
|
|
157
|
+
displaySet: displaySetInfo?.displaySet,
|
|
158
|
+
displaySetUID: displaySetInfo?.displaySetUID,
|
|
159
|
+
instance: displaySetInfo?.instance,
|
|
160
|
+
modality: displaySetInfo?.modality,
|
|
161
|
+
imageIds: displaySetInfo?.imageIds,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return candidates;
|
|
166
|
+
}
|
|
167
|
+
static getViewportDisplaySets(viewport) {
|
|
168
|
+
const displaySets = viewport.getDisplaySets?.();
|
|
169
|
+
if (!displaySets?.length) {
|
|
170
|
+
return [];
|
|
171
|
+
}
|
|
172
|
+
const provider = csUtils.genericViewportDisplaySetMetadataProvider;
|
|
173
|
+
return displaySets.map(({ displaySetId }) => {
|
|
174
|
+
const genericRegistration = provider.get(provider.VIEWPORT_V2_DISPLAY_SET, displaySetId);
|
|
175
|
+
const registeredImageIds = Array.isArray(genericRegistration)
|
|
176
|
+
? genericRegistration
|
|
177
|
+
: genericRegistration?.imageIds;
|
|
178
|
+
const typedDisplaySet = registeredImageIds
|
|
179
|
+
?.map((imageId) => metaData.get('displaySetModule', imageId))
|
|
180
|
+
.find((displaySet) => displaySet !== undefined);
|
|
181
|
+
const imageIds = typedDisplaySet?.imageIds?.length
|
|
182
|
+
? Array.from(typedDisplaySet.imageIds)
|
|
183
|
+
: registeredImageIds;
|
|
184
|
+
const registered = typedDisplaySet ?? genericRegistration;
|
|
185
|
+
const instance = typedDisplaySet?.instances?.[0] ??
|
|
186
|
+
BaseTool.getExemplarInstance(imageIds);
|
|
187
|
+
return {
|
|
188
|
+
displaySetUID: typedDisplaySet?.displaySetId ?? displaySetId,
|
|
189
|
+
displaySet: registered,
|
|
190
|
+
instance,
|
|
191
|
+
imageIds,
|
|
192
|
+
volumeId: genericRegistration?.volumeId,
|
|
193
|
+
modality: instance?.Modality ??
|
|
194
|
+
(imageIds?.length
|
|
195
|
+
? metaData.get('generalSeriesModule', imageIds[0])?.modality
|
|
196
|
+
: undefined),
|
|
197
|
+
};
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
static getExemplarInstance(imageIds) {
|
|
201
|
+
if (!imageIds?.length) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
return metaData.get('instance', imageIds[0]);
|
|
205
|
+
}
|
|
206
|
+
static findCachedStatsTargetId(data, referencedId) {
|
|
207
|
+
if (!data?.cachedStats) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
return Object.keys(data.cachedStats).find((key) => key.startsWith('volumeId:') && csUtils.getVolumeId(key) === referencedId);
|
|
211
|
+
}
|
|
212
|
+
ensureCachedStatsTargets(data, targetIds, needsUpdate) {
|
|
213
|
+
let missing = false;
|
|
214
|
+
const cachedStats = (data.cachedStats ??= {});
|
|
215
|
+
for (const targetId of targetIds) {
|
|
216
|
+
const stats = cachedStats[targetId];
|
|
217
|
+
if (!stats || needsUpdate?.(stats)) {
|
|
218
|
+
cachedStats[targetId] = {};
|
|
219
|
+
missing = true;
|
|
220
|
+
}
|
|
100
221
|
}
|
|
101
|
-
|
|
222
|
+
return missing;
|
|
102
223
|
}
|
|
103
224
|
undo() {
|
|
104
225
|
this.doneEditMemo();
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import BaseTool from './BaseTool.js';
|
|
2
2
|
import AnnotationTool from './AnnotationTool.js';
|
|
3
3
|
import AnnotationDisplayTool from './AnnotationDisplayTool.js';
|
|
4
|
-
|
|
4
|
+
import measurementTargetFilters from './measurementTargetFilters.js';
|
|
5
|
+
export { BaseTool, AnnotationTool, AnnotationDisplayTool, measurementTargetFilters, };
|
|
6
|
+
export * from './measurementTargetFilters.js';
|