@cornerstonejs/tools 1.54.2 → 1.56.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 (150) hide show
  1. package/dist/cjs/eventListeners/annotations/contourSegmentation/contourSegmentationCompleted.js +72 -74
  2. package/dist/cjs/eventListeners/annotations/contourSegmentation/contourSegmentationCompleted.js.map +1 -1
  3. package/dist/cjs/eventListeners/segmentation/imageChangeEventListener.js +15 -7
  4. package/dist/cjs/eventListeners/segmentation/imageChangeEventListener.js.map +1 -1
  5. package/dist/cjs/stateManagement/segmentation/polySeg/Labelmap/computeAndAddLabelmapRepresentation.js +1 -1
  6. package/dist/cjs/stateManagement/segmentation/polySeg/Labelmap/computeAndAddLabelmapRepresentation.js.map +1 -1
  7. package/dist/cjs/stateManagement/segmentation/polySeg/Labelmap/convertSurfaceToLabelmap.js +2 -1
  8. package/dist/cjs/stateManagement/segmentation/polySeg/Labelmap/convertSurfaceToLabelmap.js.map +1 -1
  9. package/dist/cjs/tools/annotation/LivewireContourTool.js +5 -2
  10. package/dist/cjs/tools/annotation/LivewireContourTool.js.map +1 -1
  11. package/dist/cjs/tools/annotation/PlanarFreehandROITool.js +4 -0
  12. package/dist/cjs/tools/annotation/PlanarFreehandROITool.js.map +1 -1
  13. package/dist/cjs/tools/annotation/SplineROITool.js +5 -2
  14. package/dist/cjs/tools/annotation/SplineROITool.js.map +1 -1
  15. package/dist/cjs/tools/annotation/planarFreehandROITool/drawLoop.js +2 -3
  16. package/dist/cjs/tools/annotation/planarFreehandROITool/drawLoop.js.map +1 -1
  17. package/dist/cjs/tools/base/ContourBaseTool.d.ts +8 -0
  18. package/dist/cjs/tools/base/ContourBaseTool.js +13 -2
  19. package/dist/cjs/tools/base/ContourBaseTool.js.map +1 -1
  20. package/dist/cjs/tools/segmentation/BrushTool.js +1 -1
  21. package/dist/cjs/tools/segmentation/BrushTool.js.map +1 -1
  22. package/dist/cjs/tools/segmentation/strategies/BrushStrategy.d.ts +3 -2
  23. package/dist/cjs/tools/segmentation/strategies/BrushStrategy.js +11 -4
  24. package/dist/cjs/tools/segmentation/strategies/BrushStrategy.js.map +1 -1
  25. package/dist/cjs/tools/segmentation/strategies/compositions/dynamicThreshold.js +9 -3
  26. package/dist/cjs/tools/segmentation/strategies/compositions/dynamicThreshold.js.map +1 -1
  27. package/dist/cjs/tools/segmentation/strategies/compositions/islandRemoval.js +1 -1
  28. package/dist/cjs/tools/segmentation/strategies/compositions/islandRemoval.js.map +1 -1
  29. package/dist/cjs/tools/segmentation/strategies/compositions/threshold.js +6 -2
  30. package/dist/cjs/tools/segmentation/strategies/compositions/threshold.js.map +1 -1
  31. package/dist/cjs/tools/segmentation/strategies/utils/getStrategyData.d.ts +2 -3
  32. package/dist/cjs/tools/segmentation/strategies/utils/getStrategyData.js +21 -6
  33. package/dist/cjs/tools/segmentation/strategies/utils/getStrategyData.js.map +1 -1
  34. package/dist/cjs/utilities/contours/updateContourPolyline.d.ts +5 -0
  35. package/dist/cjs/utilities/contours/updateContourPolyline.js +7 -2
  36. package/dist/cjs/utilities/contours/updateContourPolyline.js.map +1 -1
  37. package/dist/cjs/utilities/math/line/distanceToPointSquaredInfo.js.map +1 -1
  38. package/dist/cjs/utilities/math/polyline/decimate.d.ts +2 -0
  39. package/dist/cjs/utilities/math/polyline/decimate.js +73 -0
  40. package/dist/cjs/utilities/math/polyline/decimate.js.map +1 -0
  41. package/dist/cjs/utilities/math/polyline/index.d.ts +2 -1
  42. package/dist/cjs/utilities/math/polyline/index.js +3 -1
  43. package/dist/cjs/utilities/math/polyline/index.js.map +1 -1
  44. package/dist/cjs/utilities/pointInShapeCallback.js +3 -1
  45. package/dist/cjs/utilities/pointInShapeCallback.js.map +1 -1
  46. package/dist/cjs/utilities/segmentation/floodFill.js +16 -22
  47. package/dist/cjs/utilities/segmentation/floodFill.js.map +1 -1
  48. package/dist/cjs/utilities/segmentation/getSegmentAtLabelmapBorder.js +3 -2
  49. package/dist/cjs/utilities/segmentation/getSegmentAtLabelmapBorder.js.map +1 -1
  50. package/dist/cjs/utilities/segmentation/getSegmentAtWorldPoint.js +3 -2
  51. package/dist/cjs/utilities/segmentation/getSegmentAtWorldPoint.js.map +1 -1
  52. package/dist/esm/eventListeners/annotations/contourSegmentation/contourSegmentationCompleted.js +1 -1
  53. package/dist/esm/eventListeners/annotations/contourSegmentation/contourSegmentationCompleted.js.map +1 -1
  54. package/dist/esm/eventListeners/segmentation/imageChangeEventListener.js +16 -8
  55. package/dist/esm/eventListeners/segmentation/imageChangeEventListener.js.map +1 -1
  56. package/dist/esm/stateManagement/segmentation/polySeg/Labelmap/computeAndAddLabelmapRepresentation.js +1 -1
  57. package/dist/esm/stateManagement/segmentation/polySeg/Labelmap/computeAndAddLabelmapRepresentation.js.map +1 -1
  58. package/dist/esm/stateManagement/segmentation/polySeg/Labelmap/convertSurfaceToLabelmap.js +2 -1
  59. package/dist/esm/stateManagement/segmentation/polySeg/Labelmap/convertSurfaceToLabelmap.js.map +1 -1
  60. package/dist/esm/tools/annotation/LivewireContourTool.js +5 -2
  61. package/dist/esm/tools/annotation/LivewireContourTool.js.map +1 -1
  62. package/dist/esm/tools/annotation/PlanarFreehandROITool.js +4 -0
  63. package/dist/esm/tools/annotation/PlanarFreehandROITool.js.map +1 -1
  64. package/dist/esm/tools/annotation/SplineROITool.js +5 -2
  65. package/dist/esm/tools/annotation/SplineROITool.js.map +1 -1
  66. package/dist/esm/tools/annotation/planarFreehandROITool/drawLoop.js +2 -3
  67. package/dist/esm/tools/annotation/planarFreehandROITool/drawLoop.js.map +1 -1
  68. package/dist/esm/tools/base/ContourBaseTool.js +10 -0
  69. package/dist/esm/tools/base/ContourBaseTool.js.map +1 -1
  70. package/dist/esm/tools/segmentation/BrushTool.js +1 -1
  71. package/dist/esm/tools/segmentation/BrushTool.js.map +1 -1
  72. package/dist/esm/tools/segmentation/strategies/BrushStrategy.js +10 -3
  73. package/dist/esm/tools/segmentation/strategies/BrushStrategy.js.map +1 -1
  74. package/dist/esm/tools/segmentation/strategies/compositions/dynamicThreshold.js +9 -3
  75. package/dist/esm/tools/segmentation/strategies/compositions/dynamicThreshold.js.map +1 -1
  76. package/dist/esm/tools/segmentation/strategies/compositions/islandRemoval.js +1 -1
  77. package/dist/esm/tools/segmentation/strategies/compositions/islandRemoval.js.map +1 -1
  78. package/dist/esm/tools/segmentation/strategies/compositions/threshold.js +6 -2
  79. package/dist/esm/tools/segmentation/strategies/compositions/threshold.js.map +1 -1
  80. package/dist/esm/tools/segmentation/strategies/utils/getStrategyData.js +21 -6
  81. package/dist/esm/tools/segmentation/strategies/utils/getStrategyData.js.map +1 -1
  82. package/dist/esm/utilities/contours/updateContourPolyline.js +6 -2
  83. package/dist/esm/utilities/contours/updateContourPolyline.js.map +1 -1
  84. package/dist/esm/utilities/math/line/distanceToPointSquaredInfo.js.map +1 -1
  85. package/dist/esm/utilities/math/polyline/decimate.js +47 -0
  86. package/dist/esm/utilities/math/polyline/decimate.js.map +1 -0
  87. package/dist/esm/utilities/math/polyline/index.js +2 -1
  88. package/dist/esm/utilities/math/polyline/index.js.map +1 -1
  89. package/dist/esm/utilities/pointInShapeCallback.js +3 -1
  90. package/dist/esm/utilities/pointInShapeCallback.js.map +1 -1
  91. package/dist/esm/utilities/segmentation/floodFill.js +16 -22
  92. package/dist/esm/utilities/segmentation/floodFill.js.map +1 -1
  93. package/dist/esm/utilities/segmentation/getSegmentAtLabelmapBorder.js +3 -2
  94. package/dist/esm/utilities/segmentation/getSegmentAtLabelmapBorder.js.map +1 -1
  95. package/dist/esm/utilities/segmentation/getSegmentAtWorldPoint.js +3 -2
  96. package/dist/esm/utilities/segmentation/getSegmentAtWorldPoint.js.map +1 -1
  97. package/dist/types/eventListeners/segmentation/imageChangeEventListener.d.ts.map +1 -1
  98. package/dist/types/stateManagement/segmentation/polySeg/Labelmap/convertSurfaceToLabelmap.d.ts.map +1 -1
  99. package/dist/types/tools/annotation/LivewireContourTool.d.ts.map +1 -1
  100. package/dist/types/tools/annotation/PlanarFreehandROITool.d.ts.map +1 -1
  101. package/dist/types/tools/annotation/SplineROITool.d.ts.map +1 -1
  102. package/dist/types/tools/annotation/planarFreehandROITool/drawLoop.d.ts.map +1 -1
  103. package/dist/types/tools/base/ContourBaseTool.d.ts +8 -0
  104. package/dist/types/tools/base/ContourBaseTool.d.ts.map +1 -1
  105. package/dist/types/tools/segmentation/strategies/BrushStrategy.d.ts +3 -2
  106. package/dist/types/tools/segmentation/strategies/BrushStrategy.d.ts.map +1 -1
  107. package/dist/types/tools/segmentation/strategies/compositions/dynamicThreshold.d.ts.map +1 -1
  108. package/dist/types/tools/segmentation/strategies/compositions/islandRemoval.d.ts.map +1 -1
  109. package/dist/types/tools/segmentation/strategies/compositions/threshold.d.ts.map +1 -1
  110. package/dist/types/tools/segmentation/strategies/utils/getStrategyData.d.ts +2 -3
  111. package/dist/types/tools/segmentation/strategies/utils/getStrategyData.d.ts.map +1 -1
  112. package/dist/types/utilities/contours/updateContourPolyline.d.ts +5 -0
  113. package/dist/types/utilities/contours/updateContourPolyline.d.ts.map +1 -1
  114. package/dist/types/utilities/math/line/distanceToPointSquaredInfo.d.ts.map +1 -1
  115. package/dist/types/utilities/math/polyline/decimate.d.ts +3 -0
  116. package/dist/types/utilities/math/polyline/decimate.d.ts.map +1 -0
  117. package/dist/types/utilities/math/polyline/index.d.ts +2 -1
  118. package/dist/types/utilities/math/polyline/index.d.ts.map +1 -1
  119. package/dist/types/utilities/pointInShapeCallback.d.ts.map +1 -1
  120. package/dist/types/utilities/segmentation/floodFill.d.ts.map +1 -1
  121. package/dist/types/utilities/segmentation/getSegmentAtLabelmapBorder.d.ts.map +1 -1
  122. package/dist/types/utilities/segmentation/getSegmentAtWorldPoint.d.ts.map +1 -1
  123. package/dist/umd/985.index.js +1 -1
  124. package/dist/umd/985.index.js.map +1 -1
  125. package/dist/umd/index.js +1 -1
  126. package/dist/umd/index.js.map +1 -1
  127. package/package.json +7 -3
  128. package/src/eventListeners/annotations/contourSegmentation/contourSegmentationCompleted.ts +1 -1
  129. package/src/eventListeners/segmentation/imageChangeEventListener.ts +28 -13
  130. package/src/stateManagement/segmentation/polySeg/Labelmap/computeAndAddLabelmapRepresentation.ts +1 -1
  131. package/src/stateManagement/segmentation/polySeg/Labelmap/convertSurfaceToLabelmap.ts +3 -1
  132. package/src/tools/annotation/LivewireContourTool.ts +15 -2
  133. package/src/tools/annotation/PlanarFreehandROITool.ts +12 -0
  134. package/src/tools/annotation/SplineROITool.ts +13 -2
  135. package/src/tools/annotation/planarFreehandROITool/drawLoop.ts +2 -3
  136. package/src/tools/base/ContourBaseTool.ts +23 -0
  137. package/src/tools/segmentation/BrushTool.ts +1 -1
  138. package/src/tools/segmentation/strategies/BrushStrategy.ts +25 -4
  139. package/src/tools/segmentation/strategies/compositions/dynamicThreshold.ts +12 -2
  140. package/src/tools/segmentation/strategies/compositions/islandRemoval.ts +1 -2
  141. package/src/tools/segmentation/strategies/compositions/threshold.ts +8 -6
  142. package/src/tools/segmentation/strategies/utils/getStrategyData.ts +20 -5
  143. package/src/utilities/contours/updateContourPolyline.ts +23 -1
  144. package/src/utilities/math/line/distanceToPointSquaredInfo.ts +2 -1
  145. package/src/utilities/math/polyline/decimate.ts +105 -0
  146. package/src/utilities/math/polyline/index.ts +2 -0
  147. package/src/utilities/pointInShapeCallback.ts +2 -0
  148. package/src/utilities/segmentation/floodFill.ts +44 -31
  149. package/src/utilities/segmentation/getSegmentAtLabelmapBorder.ts +6 -5
  150. package/src/utilities/segmentation/getSegmentAtWorldPoint.ts +6 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cornerstonejs/tools",
3
- "version": "1.54.2",
3
+ "version": "1.56.0",
4
4
  "description": "Cornerstone3D Tools",
5
5
  "main": "src/index.ts",
6
6
  "types": "dist/types/index.d.ts",
@@ -29,12 +29,16 @@
29
29
  "webpack:watch": "webpack --mode development --progress --watch --config ./.webpack/webpack.dev.js"
30
30
  },
31
31
  "dependencies": {
32
- "@cornerstonejs/core": "^1.54.2",
32
+ "@cornerstonejs/core": "^1.56.0",
33
33
  "@icr/polyseg-wasm": "0.4.0",
34
+ "@types/offscreencanvas": "2019.7.3",
34
35
  "comlink": "^4.4.1",
35
36
  "lodash.clonedeep": "4.5.0",
36
37
  "lodash.get": "^4.4.2"
37
38
  },
39
+ "devDependencies": {
40
+ "canvas": "^2.11.2"
41
+ },
38
42
  "peerDependencies": {
39
43
  "@icr/polyseg-wasm": "0.4.0",
40
44
  "@kitware/vtk.js": "29.3.0",
@@ -55,5 +59,5 @@
55
59
  "type": "individual",
56
60
  "url": "https://ohif.org/donate"
57
61
  },
58
- "gitHead": "6e3d8ea3d2aa05751ca8dcba38716d65324cc5f4"
62
+ "gitHead": "73863e2aac88c70974cf966ca4e0eae11c1e67dc"
59
63
  }
@@ -242,7 +242,7 @@ function getContourHolesData(
242
242
  });
243
243
  }
244
244
 
245
- async function combinePolylines(
245
+ function combinePolylines(
246
246
  viewport: Types.IViewport,
247
247
  targetAnnotation: ContourSegmentationAnnotation,
248
248
  targetPolyline: Types.Point2[],
@@ -1,7 +1,7 @@
1
1
  import vtkDataArray from '@kitware/vtk.js/Common/Core/DataArray';
2
2
  import vtkImageData from '@kitware/vtk.js/Common/DataModel/ImageData';
3
3
  import {
4
- StackViewport,
4
+ BaseVolumeViewport,
5
5
  getEnabledElement,
6
6
  Enums,
7
7
  getEnabledElementByIds,
@@ -19,7 +19,7 @@ import triggerSegmentationRender from '../../utilities/segmentation/triggerSegme
19
19
  const enable = function (element: HTMLDivElement): void {
20
20
  const { viewport } = getEnabledElement(element);
21
21
 
22
- if (!(viewport instanceof StackViewport)) {
22
+ if (viewport instanceof BaseVolumeViewport) {
23
23
  return;
24
24
  }
25
25
 
@@ -35,12 +35,6 @@ const enable = function (element: HTMLDivElement): void {
35
35
  };
36
36
 
37
37
  const disable = function (element: HTMLDivElement): void {
38
- const { viewport } = getEnabledElement(element);
39
-
40
- if (!(viewport instanceof StackViewport)) {
41
- return;
42
- }
43
-
44
38
  element.removeEventListener(
45
39
  Enums.Events.STACK_NEW_IMAGE,
46
40
  _imageChangeEventListener as EventListener
@@ -152,6 +146,16 @@ function _imageChangeEventListener(evt) {
152
146
  // this means that this slice doesn't have a segmentation for this representation
153
147
  // this can be a case where the segmentation was added to certain slices only
154
148
  // so we can keep the actor but empty out the imageData
149
+ if (segmentationImageData.setDerivedImage) {
150
+ // If the image data has a set derived image, then it should be called
151
+ // to update any vtk or actor data associated with it. In this case, null
152
+ // is used to clear the data. THis allows intercepting/alternative
153
+ // to vtk calls. Eventually the vtk version should also use this.
154
+ segmentationImageData.setDerivedImage(null);
155
+ return;
156
+ }
157
+ // This is the vtk version of the clearing out the image data, and fails
158
+ // to work for non scalar image data.
155
159
  const scalarArray = vtkDataArray.newInstance({
156
160
  name: 'Pixels',
157
161
  numberOfComponents: 1,
@@ -169,7 +173,11 @@ function _imageChangeEventListener(evt) {
169
173
  const { dimensions, spacing, direction } =
170
174
  viewport.getImageDataMetadata(derivedImage);
171
175
 
172
- const currentImage = cache.getImage(currentImageId);
176
+ const currentImage =
177
+ cache.getImage(currentImageId) ||
178
+ ({
179
+ imageId: currentImageId,
180
+ } as Types.IImage);
173
181
  const { origin: currentOrigin } =
174
182
  viewport.getImageDataMetadata(currentImage);
175
183
 
@@ -230,10 +238,17 @@ function _imageChangeEventListener(evt) {
230
238
  return;
231
239
  }
232
240
 
233
- utilities.updateVTKImageDataWithCornerstoneImage(
234
- segmentationImageData,
235
- derivedImage
236
- );
241
+ if (segmentationImageData.setDerivedImage) {
242
+ // Update the derived image data, whether vtk or other as appropriate
243
+ // to the actor(s) displaying the data.
244
+ segmentationImageData.setDerivedImage(derivedImage);
245
+ } else {
246
+ // TODO - use setDerivedImage for this functionality
247
+ utilities.updateVTKImageDataWithCornerstoneImage(
248
+ segmentationImageData,
249
+ derivedImage
250
+ );
251
+ }
237
252
  viewport.render();
238
253
 
239
254
  // This is put here to make sure that the segmentation is rendered
@@ -20,6 +20,6 @@ export function computeAndAddLabelmapRepresentation(
20
20
  segmentationId,
21
21
  SegmentationRepresentations.Labelmap,
22
22
  () => computeLabelmapData(segmentationId, options),
23
- () => {}
23
+ () => undefined
24
24
  );
25
25
  }
@@ -86,4 +86,6 @@ export async function convertSurfaceToVolumeLabelmap(
86
86
  };
87
87
  }
88
88
 
89
- export async function convertSurfaceToStackLabelmap() {}
89
+ export async function convertSurfaceToStackLabelmap() {
90
+ // TODO
91
+ }
@@ -32,7 +32,6 @@ import { LivewireScissors } from '../../utilities/livewire/LivewireScissors';
32
32
  import { LivewirePath } from '../../utilities/livewire/LiveWirePath';
33
33
  import { getViewportIdsWithToolToRender } from '../../utilities/viewportFilters';
34
34
  import ContourSegmentationBaseTool from '../base/ContourSegmentationBaseTool';
35
- import updateContourPolyline from '../../utilities/contours/updateContourPolyline';
36
35
 
37
36
  const CLICK_CLOSE_CURVE_SQR_DIST = 10 ** 2; // px
38
37
 
@@ -108,6 +107,20 @@ class LivewireContourTool extends ContourSegmentationBaseTool {
108
107
  */
109
108
  showInterpolationPolyline: false,
110
109
  },
110
+
111
+ /**
112
+ * The polyline may get processed in order to reduce the number of points
113
+ * for better performance and storage.
114
+ */
115
+ decimate: {
116
+ enabled: false,
117
+ /** A maximum given distance 'epsilon' to decide if a point should or
118
+ * shouldn't be added the resulting polyline which will have a lower
119
+ * number of points for higher `epsilon` values.
120
+ */
121
+ epsilon: 0.1,
122
+ },
123
+
111
124
  actions: {
112
125
  undo: {
113
126
  method: 'undo',
@@ -922,7 +935,7 @@ class LivewireContourTool extends ContourSegmentationBaseTool {
922
935
  imagePoints = [...imagePoints, imagePoints[0]];
923
936
  }
924
937
 
925
- updateContourPolyline(
938
+ this.updateContourPolyline(
926
939
  annotation,
927
940
  {
928
941
  points: imagePoints,
@@ -231,6 +231,18 @@ class PlanarFreehandROITool extends ContourSegmentationBaseTool {
231
231
  // interpolation is complete.
232
232
  onInterpolationComplete: null,
233
233
  },
234
+ /**
235
+ * The polyline may get processed in order to reduce the number of points
236
+ * for better performance and storage.
237
+ */
238
+ decimate: {
239
+ enabled: false,
240
+ /** A maximum given distance 'epsilon' to decide if a point should or
241
+ * shouldn't be added the resulting polyline which will have a lower
242
+ * number of points for higher `epsilon` values.
243
+ */
244
+ epsilon: 0.1,
245
+ },
234
246
  calculateStats: false,
235
247
  getTextLines: defaultGetTextLines,
236
248
  statsCalculator: BasicStatsCalculator,
@@ -56,7 +56,6 @@ import { LinearSpline } from './splines/LinearSpline';
56
56
  import { CatmullRomSpline } from './splines/CatmullRomSpline';
57
57
  import { BSpline } from './splines/BSpline';
58
58
  import ContourSegmentationBaseTool from '../base/ContourSegmentationBaseTool';
59
- import updateContourPolyline from '../../utilities/contours/updateContourPolyline';
60
59
 
61
60
  const SPLINE_MIN_POINTS = 3;
62
61
  const SPLINE_CLICK_CLOSE_CURVE_DIST = 10;
@@ -121,6 +120,18 @@ class SplineROITool extends ContourSegmentationBaseTool {
121
120
  * modifier must be pressed when the first point of a new contour is added.
122
121
  */
123
122
  contourHoleAdditionModifierKey: KeyboardBindings.Shift,
123
+ /**
124
+ * The polyline may get processed in order to reduce the number of points
125
+ * for better performance and storage.
126
+ */
127
+ decimate: {
128
+ enabled: false,
129
+ /** A maximum given distance 'epsilon' to decide if a point should or
130
+ * shouldn't be added the resulting polyline which will have a lower
131
+ * number of points for higher `epsilon` values.
132
+ */
133
+ epsilon: 0.1,
134
+ },
124
135
  spline: {
125
136
  configuration: {
126
137
  [SplineTypesEnum.Cardinal]: {
@@ -693,7 +704,7 @@ class SplineROITool extends ContourSegmentationBaseTool {
693
704
  const spline = this._updateSplineInstance(element, annotation);
694
705
  const splinePolylineCanvas = spline.getPolylinePoints();
695
706
 
696
- updateContourPolyline(
707
+ this.updateContourPolyline(
697
708
  annotation,
698
709
  {
699
710
  points: splinePolylineCanvas,
@@ -18,7 +18,6 @@ import { PlanarFreehandROIAnnotation } from '../../../types/ToolSpecificAnnotati
18
18
  import findOpenUShapedContourVectorToPeak from './findOpenUShapedContourVectorToPeak';
19
19
  import { polyline } from '../../../utilities/math';
20
20
  import { removeAnnotation } from '../../../stateManagement/annotation/annotationState';
21
- import { updateContourPolyline } from '../../../utilities/contours/';
22
21
  import reverseIfAntiClockwise from '../../../utilities/contours/reverseIfAntiClockwise';
23
22
 
24
23
  const {
@@ -238,7 +237,7 @@ function completeDrawClosedContour(
238
237
  // contours. A future optimization if we use this for segmentation is to re-do
239
238
  // this rendering with the GPU rather than SVG.
240
239
 
241
- updateContourPolyline(
240
+ this.updateContourPolyline(
242
241
  annotation,
243
242
  {
244
243
  points: updatedPoints,
@@ -315,7 +314,7 @@ function completeDrawOpenContour(
315
314
  // contours. A future optimisation if we use this for segmentation is to re-do
316
315
  // this rendering with the GPU rather than SVG.
317
316
 
318
- updateContourPolyline(
317
+ this.updateContourPolyline(
319
318
  annotation,
320
319
  {
321
320
  points: updatedPoints,
@@ -17,7 +17,9 @@ import type {
17
17
  import { drawPath as drawPathSvg } from '../../drawingSvg';
18
18
  import { StyleSpecifier } from '../../types/AnnotationStyle';
19
19
  import AnnotationTool from './AnnotationTool';
20
+ import { updateContourPolyline } from '../../utilities/contours/';
20
21
  import { getContourHolesDataCanvas } from '../../utilities/contours';
22
+ import { ContourWindingDirection } from '../../types/ContourAnnotation';
21
23
 
22
24
  /**
23
25
  * A contour base class responsible for rendering contour instances such as
@@ -210,6 +212,27 @@ abstract class ContourBaseTool extends AnnotationTool {
210
212
  );
211
213
  }
212
214
 
215
+ protected updateContourPolyline(
216
+ annotation: ContourAnnotation,
217
+ polylineData: {
218
+ points: Types.Point2[];
219
+ closed?: boolean;
220
+ targetWindingDirection?: ContourWindingDirection;
221
+ },
222
+ transforms: {
223
+ canvasToWorld: (point: Types.Point2) => Types.Point3;
224
+ }
225
+ ) {
226
+ const decimateConfig = this.configuration?.decimate || {};
227
+
228
+ updateContourPolyline(annotation, polylineData, transforms, {
229
+ decimate: {
230
+ enabled: !!decimateConfig.enabled,
231
+ epsilon: decimateConfig.epsilon,
232
+ },
233
+ });
234
+ }
235
+
213
236
  /**
214
237
  * Get polyline points in world space.
215
238
  * Just to give a chance for child classes to override it.
@@ -484,7 +484,7 @@ class BrushTool extends BaseTool {
484
484
 
485
485
  this._previewData.preview = this.applyActiveStrategy(
486
486
  enabledElement,
487
- this.getOperationData()
487
+ this.getOperationData(element)
488
488
  );
489
489
  this._previewData.element = element;
490
490
  // Add a bit of time to the timer start so small accidental movements dont
@@ -15,12 +15,17 @@ import vtkImageData from '@kitware/vtk.js/Common/DataModel/ImageData';
15
15
  const { VoxelManager } = csUtils;
16
16
 
17
17
  export type InitializedOperationData = LabelmapToolOperationDataAny & {
18
+ // Allow initialization that is operation specific by keying on the name
19
+ operationName?: string;
20
+
18
21
  // Additional data for performing the strategy
19
22
  enabledElement: Types.IEnabledElement;
20
23
  centerIJK?: Types.Point3;
21
24
  centerWorld: Types.Point3;
22
25
  viewport: Types.IViewport;
23
- imageVoxelManager: csUtils.VoxelManager<number>;
26
+ imageVoxelManager:
27
+ | csUtils.VoxelManager<number>
28
+ | csUtils.VoxelManager<Types.RGB>;
24
29
  segmentationVoxelManager: csUtils.VoxelManager<number>;
25
30
  segmentationImageData: vtkImageData;
26
31
  previewVoxelManager: csUtils.VoxelManager<number>;
@@ -152,7 +157,16 @@ export default class BrushStrategy {
152
157
  enabledElement: Types.IEnabledElement,
153
158
  operationData: LabelmapToolOperationDataAny
154
159
  ) => {
155
- const initializedData = this.initialize(enabledElement, operationData);
160
+ const initializedData = this.initialize(
161
+ enabledElement,
162
+ operationData,
163
+ StrategyCallbacks.Fill
164
+ );
165
+
166
+ if (!initializedData) {
167
+ // Happens when there is no label map
168
+ return;
169
+ }
156
170
 
157
171
  const { strategySpecificConfiguration = {}, centerIJK } = initializedData;
158
172
  // Store the center IJK location so that we can skip an immediate same-point update
@@ -186,7 +200,8 @@ export default class BrushStrategy {
186
200
 
187
201
  protected initialize(
188
202
  enabledElement: Types.IEnabledElement,
189
- operationData: LabelmapToolOperationDataAny
203
+ operationData: LabelmapToolOperationDataAny,
204
+ operationName?: string
190
205
  ): InitializedOperationData {
191
206
  const { viewport } = enabledElement;
192
207
  const data = getStrategyData({ operationData, viewport });
@@ -228,6 +243,7 @@ export default class BrushStrategy {
228
243
  const previewSegmentIndex = previewEnabled ? 255 : undefined;
229
244
 
230
245
  const initializedData: InitializedOperationData = {
246
+ operationName,
231
247
  previewSegmentIndex,
232
248
  ...operationData,
233
249
  enabledElement,
@@ -263,6 +279,10 @@ export default class BrushStrategy {
263
279
  return;
264
280
  }
265
281
  const initializedData = this.initialize(enabledElement, operationData);
282
+ if (!initializedData) {
283
+ // Happens if there isn't a labelmap to apply to
284
+ return;
285
+ }
266
286
  this._onInteractionStart.forEach((func) =>
267
287
  func.call(this, initializedData)
268
288
  );
@@ -334,7 +354,8 @@ function addListMethod(name: string, createInitialized?: string) {
334
354
  ? (enabledElement, operationData) => {
335
355
  const initializedData = brushStrategy[createInitialized](
336
356
  enabledElement,
337
- operationData
357
+ operationData,
358
+ name
338
359
  );
339
360
  brushStrategy[listName].forEach((func) =>
340
361
  func.call(brushStrategy, initializedData)
@@ -1,3 +1,4 @@
1
+ import { vec3 } from 'gl-matrix';
1
2
  import type { InitializedOperationData } from '../BrushStrategy';
2
3
  import type BoundsIJK from '../../../../types/BoundsIJK';
3
4
  import StrategyCallbacks from '../../../../enums/StrategyCallbacks';
@@ -13,6 +14,7 @@ import StrategyCallbacks from '../../../../enums/StrategyCallbacks';
13
14
  export default {
14
15
  [StrategyCallbacks.Initialize]: (operationData: InitializedOperationData) => {
15
16
  const {
17
+ operationName,
16
18
  centerIJK,
17
19
  strategySpecificConfiguration,
18
20
  segmentationVoxelManager: segmentationVoxelManager,
@@ -24,6 +26,12 @@ export default {
24
26
  if (!THRESHOLD?.isDynamic || !centerIJK || !segmentIndex) {
25
27
  return;
26
28
  }
29
+ if (
30
+ operationName === StrategyCallbacks.RejectPreview ||
31
+ operationName === StrategyCallbacks.OnInteractionEnd
32
+ ) {
33
+ return;
34
+ }
27
35
 
28
36
  const { boundsIJK } = segmentationVoxelManager;
29
37
  const { threshold: oldThreshold, dynamicRadius = 0 } = THRESHOLD;
@@ -37,9 +45,11 @@ export default {
37
45
  }) as BoundsIJK;
38
46
 
39
47
  const threshold = oldThreshold || [Infinity, -Infinity];
48
+ // TODO - threshold on all three values separately
40
49
  const callback = ({ value }) => {
41
- threshold[0] = Math.min(value, threshold[0]);
42
- threshold[1] = Math.max(value, threshold[1]);
50
+ const gray = Array.isArray(value) ? vec3.len(value as any) : value;
51
+ threshold[0] = Math.min(gray, threshold[0]);
52
+ threshold[1] = Math.max(gray, threshold[1]);
43
53
  };
44
54
  imageVoxelManager.forEach(callback, { boundsIJK: nestedBounds });
45
55
 
@@ -89,8 +89,7 @@ export default {
89
89
  floodedSet.add(index);
90
90
  floodedCount++;
91
91
  };
92
-
93
- clickedPoints.forEach((clickedPoint, index) => {
92
+ clickedPoints.forEach((clickedPoint) => {
94
93
  // @ts-ignore - need to ignore the spread appication to array params
95
94
  if (getter(...clickedPoint) === 1) {
96
95
  floodFill(getter, clickedPoint, {
@@ -1,3 +1,5 @@
1
+ import { vec3 } from 'gl-matrix';
2
+ import type { Types } from '@cornerstonejs/core';
1
3
  import type { InitializedOperationData } from '../BrushStrategy';
2
4
  import StrategyCallbacks from '../../../../enums/StrategyCallbacks';
3
5
 
@@ -10,11 +12,8 @@ export default {
10
12
  [StrategyCallbacks.CreateIsInThreshold]: (
11
13
  operationData: InitializedOperationData
12
14
  ) => {
13
- const {
14
- imageVoxelManager: imageVoxelManager,
15
- strategySpecificConfiguration,
16
- segmentIndex,
17
- } = operationData;
15
+ const { imageVoxelManager, strategySpecificConfiguration, segmentIndex } =
16
+ operationData;
18
17
  if (!strategySpecificConfiguration || !segmentIndex) {
19
18
  return;
20
19
  }
@@ -23,13 +22,16 @@ export default {
23
22
  strategySpecificConfiguration;
24
23
 
25
24
  const voxelValue = imageVoxelManager.getAtIndex(index);
25
+ const gray = Array.isArray(voxelValue)
26
+ ? vec3.length(voxelValue as Types.Point3)
27
+ : voxelValue;
26
28
  // Prefer the generic version of the THRESHOLD configuration, but fallback
27
29
  // to the older THRESHOLD_INSIDE_CIRCLE version.
28
30
  const { threshold } = THRESHOLD || THRESHOLD_INSIDE_CIRCLE || {};
29
31
  if (!threshold?.length) {
30
32
  return true;
31
33
  }
32
- return threshold[0] <= voxelValue && voxelValue <= threshold[1];
34
+ return threshold[0] <= gray && gray <= threshold[1];
33
35
  };
34
36
  },
35
37
  };
@@ -9,6 +9,9 @@ function getStrategyData({ operationData, viewport }) {
9
9
  let segmentationImageData, segmentationScalarData, imageScalarData;
10
10
  let imageDimensions: Types.Point3;
11
11
  let segmentationDimensions: Types.Point3;
12
+ let imageVoxelManager;
13
+ let segmentationVoxelManager;
14
+
12
15
  if (isVolumeSegmentation(operationData, viewport)) {
13
16
  const { volumeId, referencedVolumeId } = operationData;
14
17
 
@@ -17,6 +20,7 @@ function getStrategyData({ operationData, viewport }) {
17
20
  if (!segmentationVolume) {
18
21
  return;
19
22
  }
23
+ segmentationVoxelManager = segmentationVolume.voxelManager;
20
24
 
21
25
  // we only need the referenceVolumeId if we do thresholding
22
26
  // but for other operations we don't need it so make it optional
@@ -46,32 +50,43 @@ function getStrategyData({ operationData, viewport }) {
46
50
  // and always circle modifies the current imageId which in fact is the imageData
47
51
  // of that actor at that moment so we have the imageData already
48
52
  const actor = viewport.getActor(segmentationRepresentationUID);
53
+ if (!actor) {
54
+ return;
55
+ }
49
56
  segmentationImageData = actor.actor.getMapper().getInputData();
57
+ segmentationVoxelManager = segmentationImageData.voxelManager;
50
58
  const currentSegmentationImageId = imageIdReferenceMap.get(currentImageId);
51
59
 
52
60
  const segmentationImage = cache.getImage(currentSegmentationImageId);
53
- segmentationScalarData = segmentationImage.getPixelData();
61
+ if (!segmentationImage) {
62
+ return;
63
+ }
64
+ segmentationScalarData = segmentationImage.getPixelData?.();
54
65
 
55
66
  const image = cache.getImage(currentImageId);
67
+ const imageData = image ? null : viewport.getImageData();
56
68
 
57
69
  // VERY IMPORTANT
58
70
  // This is the pixel data of the image that is being segmented in the cache
59
71
  // and we need to use this to for the modification
60
- imageScalarData = image.getPixelData();
61
- imageDimensions = [image.columns, image.rows, 1];
72
+ imageScalarData = image?.getPixelData() || imageData.getScalarData();
73
+ imageDimensions = image
74
+ ? [image.columns, image.rows, 1]
75
+ : imageData.dimensions;
62
76
  segmentationDimensions = [
63
77
  segmentationImage.columns,
64
78
  segmentationImage.rows,
65
79
  1,
66
80
  ];
81
+ imageVoxelManager = image?.voxelManager;
67
82
  }
68
83
 
69
- const segmentationVoxelManager = VoxelManager.createVolumeVoxelManager(
84
+ segmentationVoxelManager ||= VoxelManager.createVolumeVoxelManager(
70
85
  segmentationDimensions,
71
86
  segmentationScalarData
72
87
  );
73
88
 
74
- const imageVoxelManager =
89
+ imageVoxelManager ||=
75
90
  imageDimensions &&
76
91
  VoxelManager.createVolumeVoxelManager(imageDimensions, imageScalarData);
77
92
 
@@ -14,6 +14,12 @@ import {
14
14
  * @param viewport - Viewport
15
15
  * @param polylineData - Polyline data (points, winding direction and closed)
16
16
  * @param transforms - Methods to convert points to/from canvas and world spaces
17
+ * @param options - Options
18
+ * - decimate: allow to set some parameters to decimate the polyline reducing
19
+ * the amount of points stored which also affects how fast it will draw the
20
+ * annotation in a viewport, compute the winding direction, append/remove
21
+ * contours and create holes. A higher `epsilon` value results in a polyline
22
+ * with less points.
17
23
  */
18
24
  export default function updateContourPolyline(
19
25
  annotation: ContourAnnotation,
@@ -24,11 +30,27 @@ export default function updateContourPolyline(
24
30
  },
25
31
  transforms: {
26
32
  canvasToWorld: (point: Types.Point2) => Types.Point3;
33
+ },
34
+ options?: {
35
+ decimate?: {
36
+ enabled?: boolean;
37
+ epsilon?: number;
38
+ };
27
39
  }
28
40
  ) {
29
41
  const { canvasToWorld } = transforms;
30
42
  const { data } = annotation;
31
- const { points: polyline, targetWindingDirection } = polylineData;
43
+ const { targetWindingDirection } = polylineData;
44
+ let { points: polyline } = polylineData;
45
+
46
+ // Decimate the polyline to reduce tha amount of points
47
+ if (options?.decimate?.enabled) {
48
+ polyline = math.polyline.decimate(
49
+ polylineData.points,
50
+ options?.decimate?.epsilon
51
+ );
52
+ }
53
+
32
54
  let { closed } = polylineData;
33
55
  const numPoints = polyline.length;
34
56
  const polylineWorldPoints = new Array(numPoints);
@@ -24,7 +24,8 @@ export default function distanceToPointSquaredInfo(
24
24
  let closestPoint: Types.Point2;
25
25
  const distanceSquared = math.point.distanceToPointSquared(lineStart, lineEnd);
26
26
 
27
- // Check if lineStart is the same as lineEnd which means
27
+ // Check if lineStart equal to the lineEnd which means the closest point
28
+ // is any of these two points
28
29
  if (lineStart[0] === lineEnd[0] && lineStart[1] === lineEnd[1]) {
29
30
  closestPoint = lineStart;
30
31
  }