@cornerstonejs/tools 1.22.0 → 1.23.0
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/cjs/drawingSvg/drawPolyline.d.ts +2 -0
- package/dist/cjs/drawingSvg/drawPolyline.js +5 -2
- package/dist/cjs/drawingSvg/drawPolyline.js.map +1 -1
- package/dist/cjs/enums/SegmentationRepresentations.d.ts +2 -1
- package/dist/cjs/enums/SegmentationRepresentations.js +1 -0
- package/dist/cjs/enums/SegmentationRepresentations.js.map +1 -1
- package/dist/cjs/index.d.ts +2 -2
- package/dist/cjs/index.js +3 -2
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/stateManagement/segmentation/addSegmentationRepresentations.js +5 -0
- package/dist/cjs/stateManagement/segmentation/addSegmentationRepresentations.js.map +1 -1
- package/dist/cjs/tools/SegmentationIntersectionTool.d.ts +18 -0
- package/dist/cjs/tools/SegmentationIntersectionTool.js +184 -0
- package/dist/cjs/tools/SegmentationIntersectionTool.js.map +1 -0
- package/dist/cjs/tools/displayTools/Contour/addContourSetsToElement.js.map +1 -1
- package/dist/cjs/tools/displayTools/SegmentationDisplayTool.js +7 -3
- package/dist/cjs/tools/displayTools/SegmentationDisplayTool.js.map +1 -1
- package/dist/cjs/tools/displayTools/Surface/addSurfaceToElement.d.ts +2 -0
- package/dist/cjs/tools/displayTools/Surface/addSurfaceToElement.js +84 -0
- package/dist/cjs/tools/displayTools/Surface/addSurfaceToElement.js.map +1 -0
- package/dist/cjs/tools/displayTools/Surface/index.d.ts +2 -0
- package/dist/cjs/tools/displayTools/Surface/index.js +9 -0
- package/dist/cjs/tools/displayTools/Surface/index.js.map +1 -0
- package/dist/cjs/tools/displayTools/Surface/removeSurfaceFromElement.d.ts +2 -0
- package/dist/cjs/tools/displayTools/Surface/removeSurfaceFromElement.js +14 -0
- package/dist/cjs/tools/displayTools/Surface/removeSurfaceFromElement.js.map +1 -0
- package/dist/cjs/tools/displayTools/Surface/surfaceDisplay.d.ts +11 -0
- package/dist/cjs/tools/displayTools/Surface/surfaceDisplay.js +140 -0
- package/dist/cjs/tools/displayTools/Surface/surfaceDisplay.js.map +1 -0
- package/dist/cjs/tools/index.d.ts +2 -1
- package/dist/cjs/tools/index.js +3 -1
- package/dist/cjs/tools/index.js.map +1 -1
- package/dist/cjs/types/SegmentationStateTypes.d.ts +9 -1
- package/dist/cjs/types/SurfaceTypes.d.ts +4 -0
- package/dist/cjs/types/SurfaceTypes.js +3 -0
- package/dist/cjs/types/SurfaceTypes.js.map +1 -0
- package/dist/cjs/utilities/dynamicVolume/getDataInTime.js +61 -21
- package/dist/cjs/utilities/dynamicVolume/getDataInTime.js.map +1 -1
- package/dist/cjs/utilities/index.d.ts +3 -1
- package/dist/cjs/utilities/index.js +5 -1
- package/dist/cjs/utilities/index.js.map +1 -1
- package/dist/cjs/utilities/math/point/distanceToPoint.d.ts +3 -1
- package/dist/cjs/utilities/math/point/distanceToPoint.js +5 -5
- package/dist/cjs/utilities/math/point/distanceToPoint.js.map +1 -1
- package/dist/cjs/utilities/pointToString.d.ts +1 -0
- package/dist/cjs/utilities/pointToString.js +13 -0
- package/dist/cjs/utilities/pointToString.js.map +1 -0
- package/dist/cjs/utilities/polyData/utils.d.ts +4 -0
- package/dist/cjs/utilities/polyData/utils.js +58 -0
- package/dist/cjs/utilities/polyData/utils.js.map +1 -0
- package/dist/cjs/utilities/segmentation/utilities.js +2 -2
- package/dist/cjs/utilities/segmentation/utilities.js.map +1 -1
- package/dist/esm/drawingSvg/drawPolyline.d.ts +2 -0
- package/dist/esm/drawingSvg/drawPolyline.js +5 -2
- package/dist/esm/drawingSvg/drawPolyline.js.map +1 -1
- package/dist/esm/enums/SegmentationRepresentations.d.ts +2 -1
- package/dist/esm/enums/SegmentationRepresentations.js +1 -0
- package/dist/esm/enums/SegmentationRepresentations.js.map +1 -1
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/index.js +2 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/stateManagement/segmentation/addSegmentationRepresentations.js +5 -0
- package/dist/esm/stateManagement/segmentation/addSegmentationRepresentations.js.map +1 -1
- package/dist/esm/tools/SegmentationIntersectionTool.d.ts +18 -0
- package/dist/esm/tools/SegmentationIntersectionTool.js +177 -0
- package/dist/esm/tools/SegmentationIntersectionTool.js.map +1 -0
- package/dist/esm/tools/displayTools/Contour/addContourSetsToElement.js.map +1 -1
- package/dist/esm/tools/displayTools/SegmentationDisplayTool.js +7 -3
- package/dist/esm/tools/displayTools/SegmentationDisplayTool.js.map +1 -1
- package/dist/esm/tools/displayTools/Surface/addSurfaceToElement.d.ts +2 -0
- package/dist/esm/tools/displayTools/Surface/addSurfaceToElement.js +79 -0
- package/dist/esm/tools/displayTools/Surface/addSurfaceToElement.js.map +1 -0
- package/dist/esm/tools/displayTools/Surface/index.d.ts +2 -0
- package/dist/esm/tools/displayTools/Surface/index.js +3 -0
- package/dist/esm/tools/displayTools/Surface/index.js.map +1 -0
- package/dist/esm/tools/displayTools/Surface/removeSurfaceFromElement.d.ts +2 -0
- package/dist/esm/tools/displayTools/Surface/removeSurfaceFromElement.js +12 -0
- package/dist/esm/tools/displayTools/Surface/removeSurfaceFromElement.js.map +1 -0
- package/dist/esm/tools/displayTools/Surface/surfaceDisplay.d.ts +11 -0
- package/dist/esm/tools/displayTools/Surface/surfaceDisplay.js +101 -0
- package/dist/esm/tools/displayTools/Surface/surfaceDisplay.js.map +1 -0
- package/dist/esm/tools/index.d.ts +2 -1
- package/dist/esm/tools/index.js +2 -1
- package/dist/esm/tools/index.js.map +1 -1
- package/dist/esm/types/SegmentationStateTypes.d.ts +9 -1
- package/dist/esm/types/SurfaceTypes.d.ts +4 -0
- package/dist/esm/types/SurfaceTypes.js +2 -0
- package/dist/esm/types/SurfaceTypes.js.map +1 -0
- package/dist/esm/utilities/dynamicVolume/getDataInTime.js +58 -21
- package/dist/esm/utilities/dynamicVolume/getDataInTime.js.map +1 -1
- package/dist/esm/utilities/index.d.ts +3 -1
- package/dist/esm/utilities/index.js +3 -1
- package/dist/esm/utilities/index.js.map +1 -1
- package/dist/esm/utilities/math/point/distanceToPoint.d.ts +3 -1
- package/dist/esm/utilities/math/point/distanceToPoint.js +5 -5
- package/dist/esm/utilities/math/point/distanceToPoint.js.map +1 -1
- package/dist/esm/utilities/pointToString.d.ts +1 -0
- package/dist/esm/utilities/pointToString.js +9 -0
- package/dist/esm/utilities/pointToString.js.map +1 -0
- package/dist/esm/utilities/polyData/utils.d.ts +4 -0
- package/dist/esm/utilities/polyData/utils.js +52 -0
- package/dist/esm/utilities/polyData/utils.js.map +1 -0
- package/dist/esm/utilities/segmentation/utilities.js +2 -2
- package/dist/esm/utilities/segmentation/utilities.js.map +1 -1
- package/dist/umd/index.js +1 -1
- package/dist/umd/index.js.map +1 -1
- package/package.json +4 -4
- package/src/drawingSvg/drawPolyline.ts +17 -11
- package/src/enums/SegmentationRepresentations.ts +1 -1
- package/src/index.ts +2 -0
- package/src/stateManagement/segmentation/addSegmentationRepresentations.ts +8 -0
- package/src/tools/SegmentationIntersectionTool.ts +300 -0
- package/src/tools/displayTools/Contour/addContourSetsToElement.ts +1 -1
- package/src/tools/displayTools/SegmentationDisplayTool.ts +10 -4
- package/src/tools/displayTools/Surface/addSurfaceToElement.ts +115 -0
- package/src/tools/displayTools/Surface/index.ts +3 -0
- package/src/tools/displayTools/Surface/removeSurfaceFromElement.ts +35 -0
- package/src/tools/displayTools/Surface/surfaceDisplay.ts +215 -0
- package/src/tools/index.ts +2 -0
- package/src/types/SegmentationStateTypes.ts +18 -1
- package/src/types/SurfaceTypes.ts +10 -0
- package/src/utilities/dynamicVolume/generateImageFromTimeData.ts +3 -3
- package/src/utilities/dynamicVolume/getDataInTime.ts +115 -25
- package/src/utilities/index.ts +4 -0
- package/src/utilities/math/point/distanceToPoint.ts +12 -11
- package/src/utilities/pointToString.ts +10 -0
- package/src/utilities/polyData/utils.ts +82 -0
- package/src/utilities/segmentation/utilities.ts +2 -3
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cornerstonejs/tools",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.23.0",
|
|
4
4
|
"description": "Cornerstone3D Tools",
|
|
5
|
-
"main": "
|
|
5
|
+
"main": "src/index.ts",
|
|
6
6
|
"types": "dist/esm/index.d.ts",
|
|
7
7
|
"module": "dist/esm/index.js",
|
|
8
8
|
"repository": "https://github.com/cornerstonejs/cornerstone3D",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"webpack:watch": "webpack --mode development --progress --watch --config ./.webpack/webpack.dev.js"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@cornerstonejs/core": "^1.
|
|
32
|
+
"@cornerstonejs/core": "^1.23.0",
|
|
33
33
|
"lodash.clonedeep": "4.5.0",
|
|
34
34
|
"lodash.get": "^4.4.2"
|
|
35
35
|
},
|
|
@@ -52,5 +52,5 @@
|
|
|
52
52
|
"type": "individual",
|
|
53
53
|
"url": "https://ohif.org/donate"
|
|
54
54
|
},
|
|
55
|
-
"gitHead": "
|
|
55
|
+
"gitHead": "378523639b22b4aa8247118b215d68596f3dc54f"
|
|
56
56
|
}
|
|
@@ -17,6 +17,8 @@ export default function drawPolyline(
|
|
|
17
17
|
points: Types.Point2[],
|
|
18
18
|
options: {
|
|
19
19
|
color?: string;
|
|
20
|
+
fillColor?: string;
|
|
21
|
+
fillOpacity?: number;
|
|
20
22
|
width?: number;
|
|
21
23
|
lineWidth?: number;
|
|
22
24
|
lineDash?: string;
|
|
@@ -27,16 +29,19 @@ export default function drawPolyline(
|
|
|
27
29
|
return;
|
|
28
30
|
}
|
|
29
31
|
|
|
30
|
-
const { color, width, lineWidth, lineDash } =
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
32
|
+
const { fillColor, fillOpacity, color, width, lineWidth, lineDash } =
|
|
33
|
+
Object.assign(
|
|
34
|
+
{
|
|
35
|
+
color: 'dodgerblue',
|
|
36
|
+
width: '2',
|
|
37
|
+
fillColor: 'none',
|
|
38
|
+
fillOpacity: 0,
|
|
39
|
+
lineWidth: undefined,
|
|
40
|
+
lineDash: undefined,
|
|
41
|
+
connectLastToFirst: false,
|
|
42
|
+
},
|
|
43
|
+
options
|
|
44
|
+
);
|
|
40
45
|
|
|
41
46
|
// for supporting both lineWidth and width options
|
|
42
47
|
const strokeWidth = lineWidth || width;
|
|
@@ -60,7 +65,8 @@ export default function drawPolyline(
|
|
|
60
65
|
const attributes = {
|
|
61
66
|
points: pointsAttribute,
|
|
62
67
|
stroke: color,
|
|
63
|
-
fill:
|
|
68
|
+
fill: fillColor,
|
|
69
|
+
'fill-opacity': fillOpacity,
|
|
64
70
|
'stroke-width': strokeWidth,
|
|
65
71
|
'stroke-dasharray': lineDash,
|
|
66
72
|
};
|
package/src/index.ts
CHANGED
|
@@ -61,6 +61,7 @@ import {
|
|
|
61
61
|
ScaleOverlayTool,
|
|
62
62
|
OrientationMarkerTool,
|
|
63
63
|
OverlayGridTool,
|
|
64
|
+
SegmentationIntersectionTool,
|
|
64
65
|
} from './tools';
|
|
65
66
|
|
|
66
67
|
import * as Enums from './enums';
|
|
@@ -92,6 +93,7 @@ export {
|
|
|
92
93
|
CrosshairsTool,
|
|
93
94
|
ReferenceLinesTool,
|
|
94
95
|
OverlayGridTool,
|
|
96
|
+
SegmentationIntersectionTool,
|
|
95
97
|
ProbeTool,
|
|
96
98
|
RectangleROITool,
|
|
97
99
|
EllipticalROITool,
|
|
@@ -8,6 +8,7 @@ import { getToolGroup } from '../../store/ToolGroupManager';
|
|
|
8
8
|
|
|
9
9
|
import { labelmapDisplay } from '../../tools/displayTools/Labelmap';
|
|
10
10
|
import { contourDisplay } from '../../tools/displayTools/Contour';
|
|
11
|
+
import { surfaceDisplay } from '../../tools/displayTools/Surface';
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Set the specified segmentation representations on the viewports of the specified
|
|
@@ -65,6 +66,13 @@ async function _addSegmentationRepresentation(
|
|
|
65
66
|
representationInput,
|
|
66
67
|
toolGroupSpecificRepresentationConfig
|
|
67
68
|
);
|
|
69
|
+
} else if (representationInput.type === Representations.Surface) {
|
|
70
|
+
segmentationRepresentationUID =
|
|
71
|
+
await surfaceDisplay.addSegmentationRepresentation(
|
|
72
|
+
toolGroupId,
|
|
73
|
+
representationInput,
|
|
74
|
+
toolGroupSpecificRepresentationConfig
|
|
75
|
+
);
|
|
68
76
|
} else {
|
|
69
77
|
throw new Error(
|
|
70
78
|
`The representation type ${representationInput.type} is not supported`
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { getRenderingEngine } from '@cornerstonejs/core';
|
|
2
|
+
import type { Types } from '@cornerstonejs/core';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
addAnnotation,
|
|
6
|
+
getAnnotations,
|
|
7
|
+
} from '../stateManagement/annotation/annotationState';
|
|
8
|
+
import { drawPolyline } from '../drawingSvg';
|
|
9
|
+
import { getToolGroup } from '../store/ToolGroupManager';
|
|
10
|
+
import triggerAnnotationRenderForViewportIds from '../utilities/triggerAnnotationRenderForViewportIds';
|
|
11
|
+
import { PublicToolProps, ToolProps, SVGDrawingHelper } from '../types';
|
|
12
|
+
import AnnotationDisplayTool from './base/AnnotationDisplayTool';
|
|
13
|
+
import { Annotation } from '../types';
|
|
14
|
+
import { distanceToPoint } from '../utilities/math/point';
|
|
15
|
+
import { pointToString } from '../utilities/pointToString';
|
|
16
|
+
import { polyDataUtils } from '../utilities';
|
|
17
|
+
|
|
18
|
+
export interface SegmentationIntersectionAnnotation extends Annotation {
|
|
19
|
+
data: {
|
|
20
|
+
actorsWorldPointsMap: Map<string, Map<string, object>>;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
class SegmentationIntersectionTool extends AnnotationDisplayTool {
|
|
25
|
+
static toolName;
|
|
26
|
+
|
|
27
|
+
constructor(
|
|
28
|
+
toolProps: PublicToolProps = {},
|
|
29
|
+
defaultToolProps: ToolProps = {
|
|
30
|
+
configuration: {
|
|
31
|
+
opacity: 0.5,
|
|
32
|
+
},
|
|
33
|
+
}
|
|
34
|
+
) {
|
|
35
|
+
super(toolProps, defaultToolProps);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Initialize the annotation data and calculates surface intersections
|
|
40
|
+
* @returns
|
|
41
|
+
*/
|
|
42
|
+
_init = (): void => {
|
|
43
|
+
const viewportsInfo = getToolGroup(this.toolGroupId).viewportsInfo;
|
|
44
|
+
|
|
45
|
+
if (!viewportsInfo?.length) {
|
|
46
|
+
console.warn(this.getToolName() + 'Tool: No viewports found');
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const firstViewport = getRenderingEngine(
|
|
51
|
+
viewportsInfo[0].renderingEngineId
|
|
52
|
+
)?.getViewport(viewportsInfo[0].viewportId);
|
|
53
|
+
|
|
54
|
+
if (!firstViewport) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const frameOfReferenceUID = firstViewport.getFrameOfReferenceUID();
|
|
58
|
+
const annotations = getAnnotations(this.getToolName(), frameOfReferenceUID);
|
|
59
|
+
|
|
60
|
+
if (!annotations?.length) {
|
|
61
|
+
const actorsWorldPointsMap = new Map();
|
|
62
|
+
calculateSurfaceSegmentationIntersections(
|
|
63
|
+
actorsWorldPointsMap,
|
|
64
|
+
viewportsInfo
|
|
65
|
+
);
|
|
66
|
+
const newAnnotation: SegmentationIntersectionAnnotation = {
|
|
67
|
+
highlighted: true,
|
|
68
|
+
invalidated: true,
|
|
69
|
+
metadata: {
|
|
70
|
+
toolName: this.getToolName(),
|
|
71
|
+
FrameOfReferenceUID: frameOfReferenceUID,
|
|
72
|
+
referencedImageId: null,
|
|
73
|
+
},
|
|
74
|
+
data: {
|
|
75
|
+
actorsWorldPointsMap,
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
addAnnotation(newAnnotation, frameOfReferenceUID);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
triggerAnnotationRenderForViewportIds(
|
|
83
|
+
getRenderingEngine(viewportsInfo[0].renderingEngineId),
|
|
84
|
+
viewportsInfo.map(({ viewportId }) => viewportId)
|
|
85
|
+
);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
onSetToolEnabled = (): void => {
|
|
89
|
+
this._init();
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
onCameraModified = (evt: Types.EventTypes.CameraModifiedEvent): void => {
|
|
93
|
+
this._init();
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Renders the surface intersections
|
|
98
|
+
*
|
|
99
|
+
* @param enabledElement - The Cornerstone's enabledElement.
|
|
100
|
+
* @param svgDrawingHelper - The svgDrawingHelper providing the context for drawing.
|
|
101
|
+
*/
|
|
102
|
+
renderAnnotation = (
|
|
103
|
+
enabledElement: Types.IEnabledElement,
|
|
104
|
+
svgDrawingHelper: SVGDrawingHelper
|
|
105
|
+
): boolean => {
|
|
106
|
+
const { viewport, FrameOfReferenceUID } = enabledElement;
|
|
107
|
+
|
|
108
|
+
let renderStatus = false;
|
|
109
|
+
|
|
110
|
+
const annotations = getAnnotations(this.getToolName(), FrameOfReferenceUID);
|
|
111
|
+
if (!annotations?.length) {
|
|
112
|
+
return renderStatus;
|
|
113
|
+
}
|
|
114
|
+
const annotation = annotations[0];
|
|
115
|
+
const { annotationUID } = annotation;
|
|
116
|
+
const actorsWorldPointsMap = annotation.data.actorsWorldPointsMap;
|
|
117
|
+
|
|
118
|
+
calculateSurfaceSegmentationIntersectionsForViewport(
|
|
119
|
+
actorsWorldPointsMap,
|
|
120
|
+
viewport
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
const actorEntries = viewport.getActors();
|
|
124
|
+
const cacheId = getCacheId(viewport);
|
|
125
|
+
|
|
126
|
+
actorEntries.forEach((actorEntry) => {
|
|
127
|
+
if (!actorEntry?.clippingFilter) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const actorWorldPointMap = actorsWorldPointsMap.get(actorEntry.uid);
|
|
131
|
+
if (!actorWorldPointMap) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (!actorWorldPointMap.get(cacheId)) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
let polyLineIdx = 1;
|
|
138
|
+
const { worldPointsSet, color } = actorWorldPointMap.get(cacheId);
|
|
139
|
+
for (let i = 0; i < worldPointsSet.length; i++) {
|
|
140
|
+
const worldPoints = worldPointsSet[i];
|
|
141
|
+
const canvasPoints = worldPoints.map((point) =>
|
|
142
|
+
viewport.worldToCanvas(point)
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
const options = {
|
|
146
|
+
color: color,
|
|
147
|
+
fillColor: color,
|
|
148
|
+
fillOpacity: this.configuration.opacity,
|
|
149
|
+
connectLastToFirst: true,
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const polyLineUID = actorEntry.uid + '#' + polyLineIdx;
|
|
153
|
+
drawPolyline(
|
|
154
|
+
svgDrawingHelper,
|
|
155
|
+
annotationUID,
|
|
156
|
+
polyLineUID,
|
|
157
|
+
canvasPoints,
|
|
158
|
+
options
|
|
159
|
+
);
|
|
160
|
+
polyLineIdx++;
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
renderStatus = true;
|
|
165
|
+
return renderStatus;
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Calculates surface intersections points for all surface actors in a list of viewports
|
|
171
|
+
* @param actorWorldPointsMap
|
|
172
|
+
* @param viewportsInfo
|
|
173
|
+
*/
|
|
174
|
+
function calculateSurfaceSegmentationIntersections(
|
|
175
|
+
actorsWorldPointsMap,
|
|
176
|
+
viewportsInfo
|
|
177
|
+
) {
|
|
178
|
+
viewportsInfo.forEach(({ viewportId, renderingEngineId }) => {
|
|
179
|
+
const viewport =
|
|
180
|
+
getRenderingEngine(renderingEngineId)?.getViewport(viewportId);
|
|
181
|
+
calculateSurfaceSegmentationIntersectionsForViewport(
|
|
182
|
+
actorsWorldPointsMap,
|
|
183
|
+
viewport
|
|
184
|
+
);
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Calculates surface intersections points for all surface actors in a viewport
|
|
190
|
+
* generating a set of polyline points for each actor
|
|
191
|
+
* @param actorWorldPointsMap
|
|
192
|
+
* @param viewport
|
|
193
|
+
*/
|
|
194
|
+
function calculateSurfaceSegmentationIntersectionsForViewport(
|
|
195
|
+
actorsWorldPointsMap,
|
|
196
|
+
viewport
|
|
197
|
+
) {
|
|
198
|
+
const actorEntries = viewport.getActors();
|
|
199
|
+
|
|
200
|
+
// we should not use the focalPoint here, since the pan and zoom updates it,
|
|
201
|
+
// imageIndex is reliable enough
|
|
202
|
+
const cacheId = getCacheId(viewport);
|
|
203
|
+
|
|
204
|
+
actorEntries.forEach((actorEntry) => {
|
|
205
|
+
if (!actorEntry?.clippingFilter) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
let actorWorldPointsMap = actorsWorldPointsMap.get(actorEntry.uid);
|
|
210
|
+
if (!actorWorldPointsMap) {
|
|
211
|
+
actorWorldPointsMap = new Map();
|
|
212
|
+
actorsWorldPointsMap.set(actorEntry.uid, actorWorldPointsMap);
|
|
213
|
+
}
|
|
214
|
+
if (!actorWorldPointsMap.get(cacheId)) {
|
|
215
|
+
const polyData = actorEntry.clippingFilter.getOutputData();
|
|
216
|
+
const worldPointsSet = polyDataUtils.getPolyDataPoints(polyData);
|
|
217
|
+
if (!worldPointsSet) {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// worldPointsSet = removeExtraPoints(viewport, worldPointsSet);
|
|
222
|
+
const colorArray = actorEntry.actor.getProperty().getColor();
|
|
223
|
+
const color = colorToString(colorArray);
|
|
224
|
+
actorWorldPointsMap.set(cacheId, { worldPointsSet, color });
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function getCacheId(viewport) {
|
|
230
|
+
const { viewPlaneNormal } = viewport.getCamera();
|
|
231
|
+
const imageIndex = viewport.getCurrentImageIdIndex();
|
|
232
|
+
return `${viewport.id}-${pointToString(viewPlaneNormal)}-${imageIndex}`;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Transform a color array into a string
|
|
237
|
+
* @param colorArray
|
|
238
|
+
* @returns
|
|
239
|
+
*/
|
|
240
|
+
function colorToString(colorArray): string {
|
|
241
|
+
function colorComponentToString(component) {
|
|
242
|
+
let componentString = Math.floor(component * 255).toString(16);
|
|
243
|
+
if (componentString.length === 1) {
|
|
244
|
+
componentString = '0' + componentString;
|
|
245
|
+
}
|
|
246
|
+
return componentString;
|
|
247
|
+
}
|
|
248
|
+
return (
|
|
249
|
+
'#' +
|
|
250
|
+
colorComponentToString(colorArray[0]) +
|
|
251
|
+
colorComponentToString(colorArray[1]) +
|
|
252
|
+
colorComponentToString(colorArray[2])
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Remove duplicate and unnecessary points
|
|
258
|
+
* @param worldPoints
|
|
259
|
+
* @param canvasPoints
|
|
260
|
+
* @returns
|
|
261
|
+
*/
|
|
262
|
+
function removeExtraPoints(viewport, worldPointsSet) {
|
|
263
|
+
return worldPointsSet.map((worldPoints) => {
|
|
264
|
+
const canvasPoints = worldPoints.map((point) => {
|
|
265
|
+
const canvasPoint = viewport.worldToCanvas(point);
|
|
266
|
+
return [Math.floor(canvasPoint[0]), Math.floor(canvasPoint[1])];
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
let lastPoint;
|
|
270
|
+
const newWorldPoints = [];
|
|
271
|
+
let newCanvasPoints = [];
|
|
272
|
+
// removing duplicate points
|
|
273
|
+
for (let i = 0; i < worldPoints.length; i++) {
|
|
274
|
+
if (lastPoint) {
|
|
275
|
+
if (distanceToPoint(lastPoint, canvasPoints[i]) > 0) {
|
|
276
|
+
newWorldPoints.push(worldPoints[i]);
|
|
277
|
+
newCanvasPoints.push(canvasPoints[i]);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
lastPoint = canvasPoints[i];
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// checking if a middle point is near the start
|
|
284
|
+
const firstPoint = newCanvasPoints[0];
|
|
285
|
+
for (
|
|
286
|
+
let j = Math.min(30, newCanvasPoints.length);
|
|
287
|
+
j < newCanvasPoints.length;
|
|
288
|
+
j++
|
|
289
|
+
) {
|
|
290
|
+
if (distanceToPoint(firstPoint, newCanvasPoints[j]) < 0.5) {
|
|
291
|
+
newCanvasPoints = newCanvasPoints.slice(0, j);
|
|
292
|
+
return newWorldPoints.slice(0, j);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return newWorldPoints;
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
SegmentationIntersectionTool.toolName = 'SegmentationIntersection';
|
|
300
|
+
export default SegmentationIntersectionTool;
|
|
@@ -51,7 +51,7 @@ export function addContourSetsToElement(
|
|
|
51
51
|
);
|
|
52
52
|
|
|
53
53
|
const contourSet = geometry.data;
|
|
54
|
-
const polyData = getPolyData(contourSet);
|
|
54
|
+
const polyData = getPolyData(contourSet as Types.IContourSet);
|
|
55
55
|
const color = contourSet.getColor();
|
|
56
56
|
|
|
57
57
|
const size = polyData.getPoints().getNumberOfPoints();
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
SegmentationRepresentationConfig,
|
|
16
16
|
ToolGroupSpecificRepresentation,
|
|
17
17
|
} from '../../types/SegmentationStateTypes';
|
|
18
|
+
import { surfaceDisplay } from './Surface';
|
|
18
19
|
import { contourDisplay } from './Contour';
|
|
19
20
|
import { labelmapDisplay } from './Labelmap';
|
|
20
21
|
|
|
@@ -143,10 +144,15 @@ class SegmentationDisplayTool extends BaseTool {
|
|
|
143
144
|
const config = this._getMergedRepresentationsConfig(toolGroupId);
|
|
144
145
|
|
|
145
146
|
const viewportsRenderList = [];
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
147
|
+
|
|
148
|
+
const renderers = {
|
|
149
|
+
[Representations.Labelmap]: labelmapDisplay,
|
|
150
|
+
[Representations.Contour]: contourDisplay,
|
|
151
|
+
[Representations.Surface]: surfaceDisplay,
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const display = renderers[representation.type];
|
|
155
|
+
|
|
150
156
|
|
|
151
157
|
for (const viewport of toolGroupViewports) {
|
|
152
158
|
const renderedViewport = display.render(
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getEnabledElement,
|
|
3
|
+
Enums,
|
|
4
|
+
VolumeViewport3D,
|
|
5
|
+
} from '@cornerstonejs/core';
|
|
6
|
+
import vtkMapper from '@kitware/vtk.js/Rendering/Core/Mapper';
|
|
7
|
+
import vtkActor from '@kitware/vtk.js/Rendering/Core/Actor';
|
|
8
|
+
import vtkClipClosedSurface from '@kitware/vtk.js/Filters/General/ClipClosedSurface';
|
|
9
|
+
import vtkPolyData from '@kitware/vtk.js/Common/DataModel/PolyData';
|
|
10
|
+
import vtkCellArray from '@kitware/vtk.js/Common/Core/CellArray';
|
|
11
|
+
import { pointToString } from '../../../utilities/pointToString';
|
|
12
|
+
|
|
13
|
+
const polyDataCache = new Map();
|
|
14
|
+
|
|
15
|
+
function addSurfaceToElement(
|
|
16
|
+
element: HTMLDivElement,
|
|
17
|
+
surface: any,
|
|
18
|
+
actorUID: string
|
|
19
|
+
): void {
|
|
20
|
+
const enabledElement = getEnabledElement(element);
|
|
21
|
+
const { viewport } = enabledElement;
|
|
22
|
+
|
|
23
|
+
// Default to true since we are setting a new segmentation, however,
|
|
24
|
+
// in the event listener, we will make other segmentations visible/invisible
|
|
25
|
+
// based on the config
|
|
26
|
+
const points = surface.getPoints();
|
|
27
|
+
const polys = surface.getPolys();
|
|
28
|
+
const color = surface.getColor();
|
|
29
|
+
|
|
30
|
+
const polyData = vtkPolyData.newInstance();
|
|
31
|
+
polyData.getPoints().setData(points, 3);
|
|
32
|
+
|
|
33
|
+
const triangles = vtkCellArray.newInstance({
|
|
34
|
+
values: Float32Array.from(polys),
|
|
35
|
+
});
|
|
36
|
+
polyData.setPolys(triangles);
|
|
37
|
+
|
|
38
|
+
const mapper = vtkMapper.newInstance({});
|
|
39
|
+
let clippingFilter;
|
|
40
|
+
if (!(viewport instanceof VolumeViewport3D)) {
|
|
41
|
+
clippingFilter = vtkClipClosedSurface.newInstance({
|
|
42
|
+
clippingPlanes: [],
|
|
43
|
+
activePlaneId: 2,
|
|
44
|
+
passPointData: false,
|
|
45
|
+
});
|
|
46
|
+
clippingFilter.setInputData(polyData);
|
|
47
|
+
clippingFilter.setGenerateOutline(true);
|
|
48
|
+
clippingFilter.setGenerateFaces(false);
|
|
49
|
+
clippingFilter.update();
|
|
50
|
+
const filteredData = clippingFilter.getOutputData();
|
|
51
|
+
mapper.setInputData(filteredData);
|
|
52
|
+
} else {
|
|
53
|
+
mapper.setInputData(polyData);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const actor = vtkActor.newInstance();
|
|
57
|
+
actor.setMapper(mapper);
|
|
58
|
+
|
|
59
|
+
// sets the color of the surface actor
|
|
60
|
+
actor.getProperty().setColor(color[0] / 255, color[1] / 255, color[2] / 255);
|
|
61
|
+
viewport.addActor({
|
|
62
|
+
actor,
|
|
63
|
+
uid: actorUID,
|
|
64
|
+
clippingFilter,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
element.addEventListener(
|
|
68
|
+
Enums.Events.CLIPPING_PLANES_UPDATED,
|
|
69
|
+
updateSurfacePlanes
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Updates the clipping planes of a surface and caches the resulting poly data
|
|
75
|
+
* @param evt
|
|
76
|
+
*/
|
|
77
|
+
function updateSurfacePlanes(evt) {
|
|
78
|
+
const { actorEntry, vtkPlanes, viewport } = evt.detail;
|
|
79
|
+
if (!actorEntry?.clippingFilter) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const mapper = actorEntry.actor.getMapper();
|
|
84
|
+
|
|
85
|
+
const { viewPlaneNormal } = viewport.getCamera();
|
|
86
|
+
const imageIndex = viewport.getCurrentImageIdIndex();
|
|
87
|
+
|
|
88
|
+
// we should not use the focalPoint here, since the pan and zoom updates it,
|
|
89
|
+
// imageIndex is reliable enough
|
|
90
|
+
const cacheId = `${viewport.id}-${pointToString(
|
|
91
|
+
viewPlaneNormal
|
|
92
|
+
)}-${imageIndex}`;
|
|
93
|
+
|
|
94
|
+
let actorCache = polyDataCache.get(actorEntry.uid);
|
|
95
|
+
if (!actorCache) {
|
|
96
|
+
actorCache = new Map();
|
|
97
|
+
polyDataCache.set(actorEntry.uid, actorCache);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
let polyData = actorCache.get(cacheId);
|
|
101
|
+
if (!polyData) {
|
|
102
|
+
const clippingFilter = actorEntry.clippingFilter;
|
|
103
|
+
clippingFilter.setClippingPlanes(vtkPlanes);
|
|
104
|
+
try {
|
|
105
|
+
clippingFilter.update();
|
|
106
|
+
polyData = clippingFilter.getOutputData();
|
|
107
|
+
actorCache.set(cacheId, polyData);
|
|
108
|
+
} catch (e) {
|
|
109
|
+
console.error('Error clipping surface', e);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
mapper.setInputData(polyData);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export default addSurfaceToElement;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { getEnabledElement } from '@cornerstonejs/core';
|
|
2
|
+
import type { Types } from '@cornerstonejs/core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Remove the surface representation from the viewport's HTML Element.
|
|
6
|
+
* NOTE: This function should not be called directly.
|
|
7
|
+
*
|
|
8
|
+
* @param element - The element that the segmentation is being added to.
|
|
9
|
+
* @param segmentationRepresentationUID - The UID of the surface representation to remove.
|
|
10
|
+
* @param removeFromCache - boolean
|
|
11
|
+
*
|
|
12
|
+
* @internal
|
|
13
|
+
*/
|
|
14
|
+
function removeContourFromElement(
|
|
15
|
+
element: HTMLDivElement,
|
|
16
|
+
segmentationRepresentationUID: string,
|
|
17
|
+
removeFromCache = false // Todo
|
|
18
|
+
): void {
|
|
19
|
+
const enabledElement = getEnabledElement(element);
|
|
20
|
+
const { viewport } = enabledElement;
|
|
21
|
+
|
|
22
|
+
const actorEntries = (viewport as Types.IVolumeViewport).getActors();
|
|
23
|
+
|
|
24
|
+
// remove actors whose id has the same prefix as the segmentationRepresentationUID
|
|
25
|
+
const actorUIDsToRemove = actorEntries
|
|
26
|
+
.map(({ uid }) =>
|
|
27
|
+
uid.startsWith(segmentationRepresentationUID) ? uid : undefined
|
|
28
|
+
)
|
|
29
|
+
.filter(Boolean);
|
|
30
|
+
|
|
31
|
+
// @ts-ignore
|
|
32
|
+
viewport.removeActors(actorUIDsToRemove);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export default removeContourFromElement;
|