@cornerstonejs/tools 1.22.1 → 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.
Files changed (117) hide show
  1. package/dist/cjs/drawingSvg/drawPolyline.d.ts +2 -0
  2. package/dist/cjs/drawingSvg/drawPolyline.js +5 -2
  3. package/dist/cjs/drawingSvg/drawPolyline.js.map +1 -1
  4. package/dist/cjs/enums/SegmentationRepresentations.d.ts +2 -1
  5. package/dist/cjs/enums/SegmentationRepresentations.js +1 -0
  6. package/dist/cjs/enums/SegmentationRepresentations.js.map +1 -1
  7. package/dist/cjs/index.d.ts +2 -2
  8. package/dist/cjs/index.js +3 -2
  9. package/dist/cjs/index.js.map +1 -1
  10. package/dist/cjs/stateManagement/segmentation/addSegmentationRepresentations.js +5 -0
  11. package/dist/cjs/stateManagement/segmentation/addSegmentationRepresentations.js.map +1 -1
  12. package/dist/cjs/tools/SegmentationIntersectionTool.d.ts +18 -0
  13. package/dist/cjs/tools/SegmentationIntersectionTool.js +184 -0
  14. package/dist/cjs/tools/SegmentationIntersectionTool.js.map +1 -0
  15. package/dist/cjs/tools/displayTools/Contour/addContourSetsToElement.js.map +1 -1
  16. package/dist/cjs/tools/displayTools/SegmentationDisplayTool.js +7 -3
  17. package/dist/cjs/tools/displayTools/SegmentationDisplayTool.js.map +1 -1
  18. package/dist/cjs/tools/displayTools/Surface/addSurfaceToElement.d.ts +2 -0
  19. package/dist/cjs/tools/displayTools/Surface/addSurfaceToElement.js +84 -0
  20. package/dist/cjs/tools/displayTools/Surface/addSurfaceToElement.js.map +1 -0
  21. package/dist/cjs/tools/displayTools/Surface/index.d.ts +2 -0
  22. package/dist/cjs/tools/displayTools/Surface/index.js +9 -0
  23. package/dist/cjs/tools/displayTools/Surface/index.js.map +1 -0
  24. package/dist/cjs/tools/displayTools/Surface/removeSurfaceFromElement.d.ts +2 -0
  25. package/dist/cjs/tools/displayTools/Surface/removeSurfaceFromElement.js +14 -0
  26. package/dist/cjs/tools/displayTools/Surface/removeSurfaceFromElement.js.map +1 -0
  27. package/dist/cjs/tools/displayTools/Surface/surfaceDisplay.d.ts +11 -0
  28. package/dist/cjs/tools/displayTools/Surface/surfaceDisplay.js +140 -0
  29. package/dist/cjs/tools/displayTools/Surface/surfaceDisplay.js.map +1 -0
  30. package/dist/cjs/tools/index.d.ts +2 -1
  31. package/dist/cjs/tools/index.js +3 -1
  32. package/dist/cjs/tools/index.js.map +1 -1
  33. package/dist/cjs/types/SegmentationStateTypes.d.ts +9 -1
  34. package/dist/cjs/types/SurfaceTypes.d.ts +4 -0
  35. package/dist/cjs/types/SurfaceTypes.js +3 -0
  36. package/dist/cjs/types/SurfaceTypes.js.map +1 -0
  37. package/dist/cjs/utilities/index.d.ts +3 -1
  38. package/dist/cjs/utilities/index.js +5 -1
  39. package/dist/cjs/utilities/index.js.map +1 -1
  40. package/dist/cjs/utilities/math/point/distanceToPoint.d.ts +3 -1
  41. package/dist/cjs/utilities/math/point/distanceToPoint.js +5 -5
  42. package/dist/cjs/utilities/math/point/distanceToPoint.js.map +1 -1
  43. package/dist/cjs/utilities/pointToString.d.ts +1 -0
  44. package/dist/cjs/utilities/pointToString.js +13 -0
  45. package/dist/cjs/utilities/pointToString.js.map +1 -0
  46. package/dist/cjs/utilities/polyData/utils.d.ts +4 -0
  47. package/dist/cjs/utilities/polyData/utils.js +58 -0
  48. package/dist/cjs/utilities/polyData/utils.js.map +1 -0
  49. package/dist/esm/drawingSvg/drawPolyline.d.ts +2 -0
  50. package/dist/esm/drawingSvg/drawPolyline.js +5 -2
  51. package/dist/esm/drawingSvg/drawPolyline.js.map +1 -1
  52. package/dist/esm/enums/SegmentationRepresentations.d.ts +2 -1
  53. package/dist/esm/enums/SegmentationRepresentations.js +1 -0
  54. package/dist/esm/enums/SegmentationRepresentations.js.map +1 -1
  55. package/dist/esm/index.d.ts +2 -2
  56. package/dist/esm/index.js +2 -2
  57. package/dist/esm/index.js.map +1 -1
  58. package/dist/esm/stateManagement/segmentation/addSegmentationRepresentations.js +5 -0
  59. package/dist/esm/stateManagement/segmentation/addSegmentationRepresentations.js.map +1 -1
  60. package/dist/esm/tools/SegmentationIntersectionTool.d.ts +18 -0
  61. package/dist/esm/tools/SegmentationIntersectionTool.js +177 -0
  62. package/dist/esm/tools/SegmentationIntersectionTool.js.map +1 -0
  63. package/dist/esm/tools/displayTools/Contour/addContourSetsToElement.js.map +1 -1
  64. package/dist/esm/tools/displayTools/SegmentationDisplayTool.js +7 -3
  65. package/dist/esm/tools/displayTools/SegmentationDisplayTool.js.map +1 -1
  66. package/dist/esm/tools/displayTools/Surface/addSurfaceToElement.d.ts +2 -0
  67. package/dist/esm/tools/displayTools/Surface/addSurfaceToElement.js +79 -0
  68. package/dist/esm/tools/displayTools/Surface/addSurfaceToElement.js.map +1 -0
  69. package/dist/esm/tools/displayTools/Surface/index.d.ts +2 -0
  70. package/dist/esm/tools/displayTools/Surface/index.js +3 -0
  71. package/dist/esm/tools/displayTools/Surface/index.js.map +1 -0
  72. package/dist/esm/tools/displayTools/Surface/removeSurfaceFromElement.d.ts +2 -0
  73. package/dist/esm/tools/displayTools/Surface/removeSurfaceFromElement.js +12 -0
  74. package/dist/esm/tools/displayTools/Surface/removeSurfaceFromElement.js.map +1 -0
  75. package/dist/esm/tools/displayTools/Surface/surfaceDisplay.d.ts +11 -0
  76. package/dist/esm/tools/displayTools/Surface/surfaceDisplay.js +101 -0
  77. package/dist/esm/tools/displayTools/Surface/surfaceDisplay.js.map +1 -0
  78. package/dist/esm/tools/index.d.ts +2 -1
  79. package/dist/esm/tools/index.js +2 -1
  80. package/dist/esm/tools/index.js.map +1 -1
  81. package/dist/esm/types/SegmentationStateTypes.d.ts +9 -1
  82. package/dist/esm/types/SurfaceTypes.d.ts +4 -0
  83. package/dist/esm/types/SurfaceTypes.js +2 -0
  84. package/dist/esm/types/SurfaceTypes.js.map +1 -0
  85. package/dist/esm/utilities/index.d.ts +3 -1
  86. package/dist/esm/utilities/index.js +3 -1
  87. package/dist/esm/utilities/index.js.map +1 -1
  88. package/dist/esm/utilities/math/point/distanceToPoint.d.ts +3 -1
  89. package/dist/esm/utilities/math/point/distanceToPoint.js +5 -5
  90. package/dist/esm/utilities/math/point/distanceToPoint.js.map +1 -1
  91. package/dist/esm/utilities/pointToString.d.ts +1 -0
  92. package/dist/esm/utilities/pointToString.js +9 -0
  93. package/dist/esm/utilities/pointToString.js.map +1 -0
  94. package/dist/esm/utilities/polyData/utils.d.ts +4 -0
  95. package/dist/esm/utilities/polyData/utils.js +52 -0
  96. package/dist/esm/utilities/polyData/utils.js.map +1 -0
  97. package/dist/umd/index.js +1 -1
  98. package/dist/umd/index.js.map +1 -1
  99. package/package.json +4 -4
  100. package/src/drawingSvg/drawPolyline.ts +17 -11
  101. package/src/enums/SegmentationRepresentations.ts +1 -1
  102. package/src/index.ts +2 -0
  103. package/src/stateManagement/segmentation/addSegmentationRepresentations.ts +8 -0
  104. package/src/tools/SegmentationIntersectionTool.ts +300 -0
  105. package/src/tools/displayTools/Contour/addContourSetsToElement.ts +1 -1
  106. package/src/tools/displayTools/SegmentationDisplayTool.ts +10 -4
  107. package/src/tools/displayTools/Surface/addSurfaceToElement.ts +115 -0
  108. package/src/tools/displayTools/Surface/index.ts +3 -0
  109. package/src/tools/displayTools/Surface/removeSurfaceFromElement.ts +35 -0
  110. package/src/tools/displayTools/Surface/surfaceDisplay.ts +215 -0
  111. package/src/tools/index.ts +2 -0
  112. package/src/types/SegmentationStateTypes.ts +18 -1
  113. package/src/types/SurfaceTypes.ts +10 -0
  114. package/src/utilities/index.ts +4 -0
  115. package/src/utilities/math/point/distanceToPoint.ts +12 -11
  116. package/src/utilities/pointToString.ts +10 -0
  117. package/src/utilities/polyData/utils.ts +82 -0
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@cornerstonejs/tools",
3
- "version": "1.22.1",
3
+ "version": "1.23.0",
4
4
  "description": "Cornerstone3D Tools",
5
- "main": "dist/umd/index.js",
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.22.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": "c7e6f091ac9fc1e38fcaca23599b75d62af605e6"
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 } = Object.assign(
31
- {
32
- color: 'dodgerblue',
33
- width: '2',
34
- lineWidth: undefined,
35
- lineDash: undefined,
36
- connectLastToFirst: false,
37
- },
38
- options
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: 'none',
68
+ fill: fillColor,
69
+ 'fill-opacity': fillOpacity,
64
70
  'stroke-width': strokeWidth,
65
71
  'stroke-dasharray': lineDash,
66
72
  };
@@ -6,7 +6,7 @@
6
6
  enum SegmentationRepresentations {
7
7
  Labelmap = 'LABELMAP',
8
8
  Contour = 'CONTOUR',
9
- // Todo: add more representations
9
+ Surface = 'SURFACE',
10
10
  }
11
11
 
12
12
  export default SegmentationRepresentations;
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
- const display =
147
- representation.type === Representations.Labelmap
148
- ? labelmapDisplay
149
- : contourDisplay;
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,3 @@
1
+ import surfaceDisplay from './surfaceDisplay';
2
+
3
+ export { surfaceDisplay };
@@ -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;