@cornerstonejs/tools 1.42.1 → 1.43.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 (205) hide show
  1. package/dist/cjs/index.d.ts +2 -2
  2. package/dist/cjs/index.js +2 -1
  3. package/dist/cjs/index.js.map +1 -1
  4. package/dist/cjs/stateManagement/segmentation/activeSegmentation.d.ts +2 -1
  5. package/dist/cjs/stateManagement/segmentation/activeSegmentation.js +10 -1
  6. package/dist/cjs/stateManagement/segmentation/activeSegmentation.js.map +1 -1
  7. package/dist/cjs/stateManagement/segmentation/segmentIndex.js +5 -0
  8. package/dist/cjs/stateManagement/segmentation/segmentIndex.js.map +1 -1
  9. package/dist/cjs/tools/displayTools/Labelmap/labelmapConfig.js +2 -0
  10. package/dist/cjs/tools/displayTools/Labelmap/labelmapConfig.js.map +1 -1
  11. package/dist/cjs/tools/displayTools/Labelmap/labelmapDisplay.js +10 -4
  12. package/dist/cjs/tools/displayTools/Labelmap/labelmapDisplay.js.map +1 -1
  13. package/dist/cjs/tools/index.d.ts +2 -1
  14. package/dist/cjs/tools/index.js +3 -1
  15. package/dist/cjs/tools/index.js.map +1 -1
  16. package/dist/cjs/tools/segmentation/BrushTool.d.ts +1 -0
  17. package/dist/cjs/tools/segmentation/BrushTool.js +32 -15
  18. package/dist/cjs/tools/segmentation/BrushTool.js.map +1 -1
  19. package/dist/cjs/tools/segmentation/CircleScissorsTool.js +5 -0
  20. package/dist/cjs/tools/segmentation/CircleScissorsTool.js.map +1 -1
  21. package/dist/cjs/tools/segmentation/RectangleScissorsTool.js +5 -0
  22. package/dist/cjs/tools/segmentation/RectangleScissorsTool.js.map +1 -1
  23. package/dist/cjs/tools/segmentation/SegmentSelectTool.d.ts +15 -0
  24. package/dist/cjs/tools/segmentation/SegmentSelectTool.js +105 -0
  25. package/dist/cjs/tools/segmentation/SegmentSelectTool.js.map +1 -0
  26. package/dist/cjs/tools/segmentation/SphereScissorsTool.js +5 -0
  27. package/dist/cjs/tools/segmentation/SphereScissorsTool.js.map +1 -1
  28. package/dist/cjs/tools/segmentation/strategies/BrushStrategy.d.ts +2 -1
  29. package/dist/cjs/tools/segmentation/strategies/BrushStrategy.js.map +1 -1
  30. package/dist/cjs/tools/segmentation/strategies/fillCircle.js +8 -9
  31. package/dist/cjs/tools/segmentation/strategies/fillCircle.js.map +1 -1
  32. package/dist/cjs/tools/segmentation/strategies/fillRectangle.js +30 -3
  33. package/dist/cjs/tools/segmentation/strategies/fillRectangle.js.map +1 -1
  34. package/dist/cjs/tools/segmentation/strategies/fillSphere.js +3 -20
  35. package/dist/cjs/tools/segmentation/strategies/fillSphere.js.map +1 -1
  36. package/dist/cjs/types/LabelmapTypes.d.ts +1 -0
  37. package/dist/cjs/utilities/boundingBox/getBoundingBoxAroundShape.d.ts +4 -2
  38. package/dist/cjs/utilities/boundingBox/getBoundingBoxAroundShape.js +45 -23
  39. package/dist/cjs/utilities/boundingBox/getBoundingBoxAroundShape.js.map +1 -1
  40. package/dist/cjs/utilities/boundingBox/index.d.ts +2 -2
  41. package/dist/cjs/utilities/boundingBox/index.js +5 -3
  42. package/dist/cjs/utilities/boundingBox/index.js.map +1 -1
  43. package/dist/cjs/utilities/getSphereBoundsInfo.d.ts +11 -0
  44. package/dist/cjs/utilities/getSphereBoundsInfo.js +45 -0
  45. package/dist/cjs/utilities/getSphereBoundsInfo.js.map +1 -0
  46. package/dist/cjs/utilities/index.d.ts +2 -2
  47. package/dist/cjs/utilities/index.js +3 -3
  48. package/dist/cjs/utilities/index.js.map +1 -1
  49. package/dist/cjs/utilities/pointInShapeCallback.d.ts +1 -1
  50. package/dist/cjs/utilities/pointInShapeCallback.js +1 -1
  51. package/dist/cjs/utilities/pointInShapeCallback.js.map +1 -1
  52. package/dist/cjs/utilities/rectangleROITool/getBoundsIJKFromRectangleAnnotations.js +2 -2
  53. package/dist/cjs/utilities/rectangleROITool/getBoundsIJKFromRectangleAnnotations.js.map +1 -1
  54. package/dist/cjs/utilities/rectangleROITool/index.d.ts +2 -1
  55. package/dist/cjs/utilities/rectangleROITool/index.js +3 -1
  56. package/dist/cjs/utilities/rectangleROITool/index.js.map +1 -1
  57. package/dist/cjs/utilities/rectangleROITool/isAxisAlignedRectangle.d.ts +2 -0
  58. package/dist/cjs/utilities/rectangleROITool/isAxisAlignedRectangle.js +26 -0
  59. package/dist/cjs/utilities/rectangleROITool/isAxisAlignedRectangle.js.map +1 -0
  60. package/dist/cjs/utilities/segmentation/createLabelmapVolumeForViewport.js +1 -1
  61. package/dist/cjs/utilities/segmentation/createLabelmapVolumeForViewport.js.map +1 -1
  62. package/dist/cjs/utilities/segmentation/index.d.ts +2 -1
  63. package/dist/cjs/utilities/segmentation/index.js +3 -1
  64. package/dist/cjs/utilities/segmentation/index.js.map +1 -1
  65. package/dist/cjs/utilities/segmentation/invalidateBrushCursor.d.ts +1 -0
  66. package/dist/cjs/utilities/segmentation/invalidateBrushCursor.js +31 -0
  67. package/dist/cjs/utilities/segmentation/invalidateBrushCursor.js.map +1 -0
  68. package/dist/cjs/utilities/segmentation/utilities.d.ts +1 -1
  69. package/dist/cjs/utilities/segmentation/utilities.js +2 -2
  70. package/dist/cjs/utilities/segmentation/utilities.js.map +1 -1
  71. package/dist/esm/index.js +2 -2
  72. package/dist/esm/index.js.map +1 -1
  73. package/dist/esm/stateManagement/segmentation/activeSegmentation.js +10 -2
  74. package/dist/esm/stateManagement/segmentation/activeSegmentation.js.map +1 -1
  75. package/dist/esm/stateManagement/segmentation/segmentIndex.js +6 -1
  76. package/dist/esm/stateManagement/segmentation/segmentIndex.js.map +1 -1
  77. package/dist/esm/tools/displayTools/Labelmap/labelmapConfig.js +2 -0
  78. package/dist/esm/tools/displayTools/Labelmap/labelmapConfig.js.map +1 -1
  79. package/dist/esm/tools/displayTools/Labelmap/labelmapDisplay.js +11 -5
  80. package/dist/esm/tools/displayTools/Labelmap/labelmapDisplay.js.map +1 -1
  81. package/dist/esm/tools/index.js +2 -1
  82. package/dist/esm/tools/index.js.map +1 -1
  83. package/dist/esm/tools/segmentation/BrushTool.js +31 -15
  84. package/dist/esm/tools/segmentation/BrushTool.js.map +1 -1
  85. package/dist/esm/tools/segmentation/CircleScissorsTool.js +5 -0
  86. package/dist/esm/tools/segmentation/CircleScissorsTool.js.map +1 -1
  87. package/dist/esm/tools/segmentation/RectangleScissorsTool.js +5 -0
  88. package/dist/esm/tools/segmentation/RectangleScissorsTool.js.map +1 -1
  89. package/dist/esm/tools/segmentation/SegmentSelectTool.js +100 -0
  90. package/dist/esm/tools/segmentation/SegmentSelectTool.js.map +1 -0
  91. package/dist/esm/tools/segmentation/SphereScissorsTool.js +5 -0
  92. package/dist/esm/tools/segmentation/SphereScissorsTool.js.map +1 -1
  93. package/dist/esm/tools/segmentation/strategies/BrushStrategy.js.map +1 -1
  94. package/dist/esm/tools/segmentation/strategies/fillCircle.js +8 -9
  95. package/dist/esm/tools/segmentation/strategies/fillCircle.js.map +1 -1
  96. package/dist/esm/tools/segmentation/strategies/fillRectangle.js +32 -5
  97. package/dist/esm/tools/segmentation/strategies/fillRectangle.js.map +1 -1
  98. package/dist/esm/tools/segmentation/strategies/fillSphere.js +3 -20
  99. package/dist/esm/tools/segmentation/strategies/fillSphere.js.map +1 -1
  100. package/dist/esm/utilities/boundingBox/getBoundingBoxAroundShape.js +40 -23
  101. package/dist/esm/utilities/boundingBox/getBoundingBoxAroundShape.js.map +1 -1
  102. package/dist/esm/utilities/boundingBox/index.js +2 -2
  103. package/dist/esm/utilities/boundingBox/index.js.map +1 -1
  104. package/dist/esm/utilities/getSphereBoundsInfo.js +42 -0
  105. package/dist/esm/utilities/getSphereBoundsInfo.js.map +1 -0
  106. package/dist/esm/utilities/index.js +2 -2
  107. package/dist/esm/utilities/index.js.map +1 -1
  108. package/dist/esm/utilities/pointInShapeCallback.js +1 -1
  109. package/dist/esm/utilities/pointInShapeCallback.js.map +1 -1
  110. package/dist/esm/utilities/rectangleROITool/getBoundsIJKFromRectangleAnnotations.js +2 -2
  111. package/dist/esm/utilities/rectangleROITool/getBoundsIJKFromRectangleAnnotations.js.map +1 -1
  112. package/dist/esm/utilities/rectangleROITool/index.js +2 -1
  113. package/dist/esm/utilities/rectangleROITool/index.js.map +1 -1
  114. package/dist/esm/utilities/rectangleROITool/isAxisAlignedRectangle.js +23 -0
  115. package/dist/esm/utilities/rectangleROITool/isAxisAlignedRectangle.js.map +1 -0
  116. package/dist/esm/utilities/segmentation/createLabelmapVolumeForViewport.js +1 -1
  117. package/dist/esm/utilities/segmentation/createLabelmapVolumeForViewport.js.map +1 -1
  118. package/dist/esm/utilities/segmentation/index.js +2 -1
  119. package/dist/esm/utilities/segmentation/index.js.map +1 -1
  120. package/dist/esm/utilities/segmentation/invalidateBrushCursor.js +24 -0
  121. package/dist/esm/utilities/segmentation/invalidateBrushCursor.js.map +1 -0
  122. package/dist/esm/utilities/segmentation/utilities.js +2 -2
  123. package/dist/esm/utilities/segmentation/utilities.js.map +1 -1
  124. package/dist/types/index.d.ts +2 -2
  125. package/dist/types/index.d.ts.map +1 -1
  126. package/dist/types/stateManagement/segmentation/activeSegmentation.d.ts +2 -1
  127. package/dist/types/stateManagement/segmentation/activeSegmentation.d.ts.map +1 -1
  128. package/dist/types/stateManagement/segmentation/segmentIndex.d.ts.map +1 -1
  129. package/dist/types/tools/displayTools/Labelmap/labelmapConfig.d.ts.map +1 -1
  130. package/dist/types/tools/displayTools/Labelmap/labelmapDisplay.d.ts.map +1 -1
  131. package/dist/types/tools/index.d.ts +2 -1
  132. package/dist/types/tools/index.d.ts.map +1 -1
  133. package/dist/types/tools/segmentation/BrushTool.d.ts +1 -0
  134. package/dist/types/tools/segmentation/BrushTool.d.ts.map +1 -1
  135. package/dist/types/tools/segmentation/CircleScissorsTool.d.ts.map +1 -1
  136. package/dist/types/tools/segmentation/RectangleScissorsTool.d.ts.map +1 -1
  137. package/dist/types/tools/segmentation/SegmentSelectTool.d.ts +16 -0
  138. package/dist/types/tools/segmentation/SegmentSelectTool.d.ts.map +1 -0
  139. package/dist/types/tools/segmentation/SphereScissorsTool.d.ts.map +1 -1
  140. package/dist/types/tools/segmentation/strategies/BrushStrategy.d.ts +2 -1
  141. package/dist/types/tools/segmentation/strategies/BrushStrategy.d.ts.map +1 -1
  142. package/dist/types/tools/segmentation/strategies/fillCircle.d.ts.map +1 -1
  143. package/dist/types/tools/segmentation/strategies/fillRectangle.d.ts.map +1 -1
  144. package/dist/types/tools/segmentation/strategies/fillSphere.d.ts.map +1 -1
  145. package/dist/types/types/LabelmapTypes.d.ts +1 -0
  146. package/dist/types/types/LabelmapTypes.d.ts.map +1 -1
  147. package/dist/types/utilities/boundingBox/getBoundingBoxAroundShape.d.ts +4 -2
  148. package/dist/types/utilities/boundingBox/getBoundingBoxAroundShape.d.ts.map +1 -1
  149. package/dist/types/utilities/boundingBox/index.d.ts +2 -2
  150. package/dist/types/utilities/boundingBox/index.d.ts.map +1 -1
  151. package/dist/types/utilities/getSphereBoundsInfo.d.ts +12 -0
  152. package/dist/types/utilities/getSphereBoundsInfo.d.ts.map +1 -0
  153. package/dist/types/utilities/index.d.ts +2 -2
  154. package/dist/types/utilities/index.d.ts.map +1 -1
  155. package/dist/types/utilities/pointInShapeCallback.d.ts +1 -1
  156. package/dist/types/utilities/rectangleROITool/getBoundsIJKFromRectangleAnnotations.d.ts.map +1 -1
  157. package/dist/types/utilities/rectangleROITool/index.d.ts +2 -1
  158. package/dist/types/utilities/rectangleROITool/index.d.ts.map +1 -1
  159. package/dist/types/utilities/rectangleROITool/isAxisAlignedRectangle.d.ts +3 -0
  160. package/dist/types/utilities/rectangleROITool/isAxisAlignedRectangle.d.ts.map +1 -0
  161. package/dist/types/utilities/segmentation/index.d.ts +2 -1
  162. package/dist/types/utilities/segmentation/index.d.ts.map +1 -1
  163. package/dist/types/utilities/segmentation/invalidateBrushCursor.d.ts +2 -0
  164. package/dist/types/utilities/segmentation/invalidateBrushCursor.d.ts.map +1 -0
  165. package/dist/types/utilities/segmentation/utilities.d.ts +1 -1
  166. package/dist/types/utilities/segmentation/utilities.d.ts.map +1 -1
  167. package/dist/umd/index.js +1 -1
  168. package/dist/umd/index.js.map +1 -1
  169. package/package.json +4 -4
  170. package/src/index.ts +2 -0
  171. package/src/stateManagement/segmentation/activeSegmentation.ts +24 -1
  172. package/src/stateManagement/segmentation/segmentIndex.ts +15 -2
  173. package/src/tools/displayTools/Labelmap/labelmapConfig.ts +2 -0
  174. package/src/tools/displayTools/Labelmap/labelmapDisplay.ts +23 -12
  175. package/src/tools/index.ts +2 -0
  176. package/src/tools/segmentation/BrushTool.ts +53 -26
  177. package/src/tools/segmentation/CircleScissorsTool.ts +9 -0
  178. package/src/tools/segmentation/RectangleScissorsTool.ts +10 -1
  179. package/src/tools/segmentation/SegmentSelectTool.ts +182 -0
  180. package/src/tools/segmentation/SphereScissorsTool.ts +9 -0
  181. package/src/tools/segmentation/strategies/BrushStrategy.ts +2 -1
  182. package/src/tools/segmentation/strategies/fillCircle.ts +15 -14
  183. package/src/tools/segmentation/strategies/fillRectangle.ts +51 -6
  184. package/src/tools/segmentation/strategies/fillSphere.ts +10 -38
  185. package/src/types/LabelmapTypes.ts +5 -2
  186. package/src/utilities/boundingBox/getBoundingBoxAroundShape.ts +88 -37
  187. package/src/utilities/boundingBox/index.ts +11 -2
  188. package/src/utilities/{pointInSurroundingSphereCallback.ts → getSphereBoundsInfo.ts} +39 -82
  189. package/src/utilities/index.ts +2 -2
  190. package/src/utilities/math/ellipse/pointInEllipse.ts +1 -1
  191. package/src/utilities/pointInShapeCallback.ts +2 -2
  192. package/src/utilities/rectangleROITool/getBoundsIJKFromRectangleAnnotations.ts +5 -2
  193. package/src/utilities/rectangleROITool/index.ts +2 -1
  194. package/src/utilities/rectangleROITool/isAxisAlignedRectangle.ts +53 -0
  195. package/src/utilities/segmentation/createLabelmapVolumeForViewport.ts +1 -1
  196. package/src/utilities/segmentation/index.ts +2 -0
  197. package/src/utilities/segmentation/invalidateBrushCursor.ts +48 -0
  198. package/src/utilities/segmentation/utilities.ts +5 -2
  199. package/dist/cjs/utilities/pointInSurroundingSphereCallback.d.ts +0 -4
  200. package/dist/cjs/utilities/pointInSurroundingSphereCallback.js +0 -70
  201. package/dist/cjs/utilities/pointInSurroundingSphereCallback.js.map +0 -1
  202. package/dist/esm/utilities/pointInSurroundingSphereCallback.js +0 -64
  203. package/dist/esm/utilities/pointInSurroundingSphereCallback.js.map +0 -1
  204. package/dist/types/utilities/pointInSurroundingSphereCallback.d.ts +0 -5
  205. package/dist/types/utilities/pointInSurroundingSphereCallback.d.ts.map +0 -1
@@ -6,20 +6,20 @@ import {
6
6
  getCanvasEllipseCorners,
7
7
  precalculatePointInEllipse,
8
8
  } from '../../../utilities/math/ellipse';
9
- import pointInSphere from '../../../utilities/math/sphere/pointInSphere';
10
- import { getBoundingBoxAroundShape } from '../../../utilities/boundingBox';
9
+ import { getBoundingBoxAroundShapeIJK } from '../../../utilities/boundingBox';
11
10
  import BrushStrategy from './BrushStrategy';
12
11
  import type { Composition, InitializedOperationData } from './BrushStrategy';
13
12
  import type { CanvasCoordinates } from '../../../types';
14
13
  import { StrategyCallbacks } from '../../../enums';
15
14
  import compositions from './compositions';
15
+ import { pointInSphere } from '../../../utilities/math/sphere';
16
16
 
17
17
  const { transformWorldToIndex, isEqual } = csUtils;
18
18
 
19
19
  const initializeCircle = {
20
20
  [StrategyCallbacks.Initialize]: (operationData: InitializedOperationData) => {
21
21
  const {
22
- points,
22
+ points, // bottom, top, left, right
23
23
  imageVoxelManager: imageVoxelManager,
24
24
  viewport,
25
25
  segmentationImageData,
@@ -42,6 +42,7 @@ const initializeCircle = {
42
42
  segmentationImageData,
43
43
  center as Types.Point3
44
44
  );
45
+
45
46
  const canvasCoordinates = points.map((p) =>
46
47
  viewport.worldToCanvas(p)
47
48
  ) as CanvasCoordinates;
@@ -55,17 +56,18 @@ const initializeCircle = {
55
56
  const topLeftWorld = viewport.canvasToWorld(topLeftCanvas);
56
57
  const bottomRightWorld = viewport.canvasToWorld(bottomRightCanvas);
57
58
 
58
- const ellipsoidCornersIJK = [
59
- <Types.Point3>transformWorldToIndex(segmentationImageData, topLeftWorld),
60
- <Types.Point3>(
61
- transformWorldToIndex(segmentationImageData, bottomRightWorld)
62
- ),
63
- ];
59
+ const circleCornersIJK = points.map((world) => {
60
+ return transformWorldToIndex(segmentationImageData, world);
61
+ });
64
62
 
65
- segmentationVoxelManager.boundsIJK = getBoundingBoxAroundShape(
66
- ellipsoidCornersIJK,
67
- segmentationVoxelManager.dimensions
63
+ // get the bounds from the circle points since in oblique images the
64
+ // circle will not be axis aligned
65
+ const boundsIJK = getBoundingBoxAroundShapeIJK(
66
+ circleCornersIJK,
67
+ segmentationImageData.getDimensions()
68
68
  );
69
+
70
+ segmentationVoxelManager.boundsIJK = boundsIJK;
69
71
  imageVoxelManager.isInObject = createPointInEllipse({
70
72
  topLeftWorld,
71
73
  bottomRightWorld,
@@ -113,9 +115,8 @@ function createPointInEllipse(worldInfo: {
113
115
  yRadius,
114
116
  zRadius,
115
117
  };
116
- const inverts = precalculatePointInEllipse(ellipseObj);
117
- const { precalculated } = inverts;
118
118
 
119
+ const { precalculated } = precalculatePointInEllipse(ellipseObj, {});
119
120
  return precalculated;
120
121
  }
121
122
 
@@ -1,11 +1,15 @@
1
- import { utilities as csUtils } from '@cornerstonejs/core';
1
+ import { utilities as csUtils, StackViewport } from '@cornerstonejs/core';
2
2
  import type { Types } from '@cornerstonejs/core';
3
3
 
4
- import { getBoundingBoxAroundShape } from '../../../utilities/boundingBox';
4
+ import {
5
+ getBoundingBoxAroundShapeIJK,
6
+ getBoundingBoxAroundShapeWorld,
7
+ } from '../../../utilities/boundingBox';
5
8
  import { pointInShapeCallback } from '../../../utilities';
6
9
  import { triggerSegmentationDataModified } from '../../../stateManagement/segmentation/triggerSegmentationEvents';
7
10
  import { LabelmapToolOperationData } from '../../../types';
8
11
  import { getStrategyData } from './utils/getStrategyData';
12
+ import { isAxisAlignedRectangle } from '../../../utilities/rectangleROITool/isAxisAlignedRectangle';
9
13
 
10
14
  const { transformWorldToIndex } = csUtils;
11
15
 
@@ -29,6 +33,7 @@ function fillRectangle(
29
33
  const { points, segmentsLocked, segmentIndex, segmentationId } =
30
34
  operationData;
31
35
 
36
+ const { viewport } = enabledElement;
32
37
  const strategyData = getStrategyData({
33
38
  operationData,
34
39
  viewport: enabledElement.viewport,
@@ -52,13 +57,53 @@ function fillRectangle(
52
57
  });
53
58
  });
54
59
 
55
- const boundsIJK = getBoundingBoxAroundShape(
60
+ const boundsIJK = getBoundingBoxAroundShapeIJK(
56
61
  rectangleCornersIJK,
57
62
  segmentationImageData.getDimensions()
58
63
  );
59
64
 
60
- // Since always all points inside the boundsIJK is inside the rectangle...
61
- const pointInRectangle = () => true;
65
+ const isStackViewport = viewport instanceof StackViewport;
66
+
67
+ // Are we working with 2D rectangle in axis aligned viewport view or not
68
+ const isAligned =
69
+ isStackViewport || isAxisAlignedRectangle(rectangleCornersIJK);
70
+
71
+ const direction = segmentationImageData.getDirection();
72
+ const spacing = segmentationImageData.getSpacing();
73
+ const { viewPlaneNormal } = viewport.getCamera();
74
+
75
+ // In case that we are working on oblique, our EPS is really the spacing in the
76
+ // normal direction, since we can't really test each voxel against a 2D rectangle
77
+ // we need some tolerance in the normal direction.
78
+ const EPS = csUtils.getSpacingInNormalDirection(
79
+ {
80
+ direction,
81
+ spacing,
82
+ },
83
+ viewPlaneNormal
84
+ );
85
+
86
+ const pointsBoundsLPS = getBoundingBoxAroundShapeWorld(points);
87
+ let [[xMin, xMax], [yMin, yMax], [zMin, zMax]] = pointsBoundsLPS;
88
+
89
+ // Update the bounds with +/- EPS
90
+ xMin -= EPS;
91
+ xMax += EPS;
92
+ yMin -= EPS;
93
+ yMax += EPS;
94
+ zMin -= EPS;
95
+ zMax += EPS;
96
+
97
+ const pointInShapeFn = isAligned
98
+ ? () => true
99
+ : (pointLPS) => {
100
+ const [x, y, z] = pointLPS;
101
+ const xInside = x >= xMin && x <= xMax;
102
+ const yInside = y >= yMin && y <= yMax;
103
+ const zInside = z >= zMin && z <= zMax;
104
+
105
+ return xInside && yInside && zInside;
106
+ };
62
107
 
63
108
  const callback = ({ value, index }) => {
64
109
  if (segmentsLocked.includes(value)) {
@@ -70,7 +115,7 @@ function fillRectangle(
70
115
 
71
116
  pointInShapeCallback(
72
117
  segmentationImageData,
73
- pointInRectangle,
118
+ pointInShapeFn,
74
119
  callback,
75
120
  boundsIJK
76
121
  );
@@ -2,16 +2,13 @@ import type { Types } from '@cornerstonejs/core';
2
2
  import { utilities as csUtils } from '@cornerstonejs/core';
3
3
  import { vec3 } from 'gl-matrix';
4
4
 
5
- import { getCanvasEllipseCorners } from '../../../utilities/math/ellipse';
6
- import { getBoundingBoxAroundShape } from '../../../utilities/boundingBox';
7
5
  import BrushStrategy from './BrushStrategy';
8
6
  import type { InitializedOperationData, Composition } from './BrushStrategy';
9
- import type { CanvasCoordinates } from '../../../types';
10
7
  import compositions from './compositions';
11
8
  import StrategyCallbacks from '../../../enums/StrategyCallbacks';
12
9
  import { createEllipseInPoint } from './fillCircle';
13
10
  const { transformWorldToIndex } = csUtils;
14
-
11
+ import { getSphereBoundsInfo } from '../../../utilities/getSphereBoundsInfo';
15
12
  const sphereComposition = {
16
13
  [StrategyCallbacks.Initialize]: (operationData: InitializedOperationData) => {
17
14
  const {
@@ -38,43 +35,18 @@ const sphereComposition = {
38
35
  segmentationImageData,
39
36
  center as Types.Point3
40
37
  );
41
- const canvasCoordinates = points.map((p) =>
42
- viewport.worldToCanvas(p)
43
- ) as CanvasCoordinates;
44
-
45
- // 1. From the drawn tool: Get the ellipse (circle) topLeft and bottomRight
46
- // corners in canvas coordinates
47
- const [topLeftCanvas, bottomRightCanvas] =
48
- getCanvasEllipseCorners(canvasCoordinates);
49
38
 
50
- // 2. Find the extent of the ellipse (circle) in IJK index space of the image
51
- const topLeftWorld = viewport.canvasToWorld(topLeftCanvas);
52
- const bottomRightWorld = viewport.canvasToWorld(bottomRightCanvas);
53
- // This will be 2d, now expand to 3d
54
- const diameters = topLeftWorld.map((left, index) =>
55
- Math.abs(bottomRightWorld[index] - left)
39
+ const {
40
+ boundsIJK: newBoundsIJK,
41
+ topLeftWorld,
42
+ bottomRightWorld,
43
+ } = getSphereBoundsInfo(
44
+ points.slice(0, 2) as [Types.Point3, Types.Point3],
45
+ segmentationImageData,
46
+ viewport
56
47
  );
57
- const radius = Math.max(...diameters) / 2;
58
- // Make 3d sphere
59
- topLeftWorld.forEach((left, index) => {
60
- const right = bottomRightWorld[index];
61
- if (left === right) {
62
- topLeftWorld[index] = left - radius;
63
- bottomRightWorld[index] = left + radius;
64
- }
65
- });
66
48
 
67
- const ellipsoidCornersIJK = [
68
- <Types.Point3>transformWorldToIndex(segmentationImageData, topLeftWorld),
69
- <Types.Point3>(
70
- transformWorldToIndex(segmentationImageData, bottomRightWorld)
71
- ),
72
- ];
73
-
74
- segmentationVoxelManager.boundsIJK = getBoundingBoxAroundShape(
75
- ellipsoidCornersIJK,
76
- segmentationVoxelManager.dimensions
77
- );
49
+ segmentationVoxelManager.boundsIJK = newBoundsIJK;
78
50
 
79
51
  imageVoxelManager.isInObject = createEllipseInPoint({
80
52
  topLeftWorld,
@@ -7,10 +7,13 @@ import type { vtkPiecewiseFunction } from '@kitware/vtk.js/Common/DataModel/Piec
7
7
  export type LabelmapConfig = {
8
8
  /** whether to render segmentation outline */
9
9
  renderOutline?: boolean;
10
- /** thickness of the outline when segmentation is active */
10
+ /** thickness of the outline when segmentation is active - all segments */
11
11
  outlineWidthActive?: number;
12
- /** thickness of the outline when segmentation is inactive */
12
+ /** thickness of the outline when segmentation is inactive - all segments */
13
13
  outlineWidthInactive?: number;
14
+ /** delta thickness of the active segment index outline (0 means same thickness,
15
+ * 1 means 1px thicker, -1 means 1px thinner) */
16
+ activeSegmentOutlineWidthDelta?: number;
14
17
  /** whether to render segmentation filling */
15
18
  renderFill?: boolean;
16
19
  /** whether to render segmentation filling when inactive */
@@ -1,57 +1,108 @@
1
1
  import type { Types } from '@cornerstonejs/core';
2
+ import { CONSTANTS } from '@cornerstonejs/core';
2
3
 
3
- /**
4
- * With a given vertices (points) coordinates in IJK, it calculates the minimum and maximum
5
- * coordinate in each axis, and returns them. If dimensions are provided it also
6
- * clip the min, max to the provided width, height and depth
7
- *
8
- * @param points - shape corner points coordinates (IJK)
9
- * @param dimensions - dimensions of the image
10
- * @returns [[xMin,xMax],[yMin,yMax], [zMin,zMax]]
11
- */
12
- function getBoundingBoxAroundShape(
13
- points: Types.Point3[],
14
- dimensions?: Types.Point3
15
- ): [Types.Point2, Types.Point2, Types.Point2] {
4
+ const { EPSILON } = CONSTANTS;
5
+
6
+ /** Bounding box type */
7
+ type BoundingBox =
8
+ | [Types.Point2, Types.Point2, null]
9
+ | [Types.Point2, Types.Point2, Types.Point2];
10
+
11
+ function calculateBoundingBox(
12
+ points,
13
+ dimensions,
14
+ isWorld = false
15
+ ): BoundingBox {
16
16
  let xMin = Infinity;
17
- let xMax = 0;
17
+ let xMax = isWorld ? -Infinity : 0;
18
18
  let yMin = Infinity;
19
- let yMax = 0;
19
+ let yMax = isWorld ? -Infinity : 0;
20
20
  let zMin = Infinity;
21
- let zMax = 0;
21
+ let zMax = isWorld ? -Infinity : 0;
22
+
23
+ const is3D = points[0]?.length === 3;
22
24
 
23
25
  points.forEach((p) => {
24
26
  xMin = Math.min(p[0], xMin);
25
27
  xMax = Math.max(p[0], xMax);
26
28
  yMin = Math.min(p[1], yMin);
27
29
  yMax = Math.max(p[1], yMax);
28
- zMin = Math.min(p[2], zMin);
29
- zMax = Math.max(p[2], zMax);
30
- });
31
30
 
32
- xMin = Math.floor(xMin);
33
- xMax = Math.floor(xMax);
34
- yMin = Math.floor(yMin);
35
- yMax = Math.floor(yMax);
36
- zMin = Math.floor(zMin);
37
- zMax = Math.floor(zMax);
31
+ if (is3D) {
32
+ zMin = Math.min(p[2] ?? zMin, zMin);
33
+ zMax = Math.max(p[2] ?? zMax, zMax);
34
+ }
35
+ });
38
36
 
39
37
  if (dimensions) {
40
- // clip the min, max to the provided width, height and depth
41
- const [width, height, depth] = dimensions;
38
+ xMin = Math.max(isWorld ? dimensions[0] + EPSILON : 0, xMin);
39
+ xMax = Math.min(
40
+ isWorld ? dimensions[0] - EPSILON : dimensions[0] - 1,
41
+ xMax
42
+ );
43
+ yMin = Math.max(isWorld ? dimensions[1] + EPSILON : 0, yMin);
44
+ yMax = Math.min(
45
+ isWorld ? dimensions[1] - EPSILON : dimensions[1] - 1,
46
+ yMax
47
+ );
48
+
49
+ if (is3D && dimensions.length === 3) {
50
+ zMin = Math.max(isWorld ? dimensions[2] + EPSILON : 0, zMin);
51
+ zMax = Math.min(
52
+ isWorld ? dimensions[2] - EPSILON : dimensions[2] - 1,
53
+ zMax
54
+ );
55
+ }
56
+ } else if (!isWorld) {
57
+ // still need to bound to 0 and Infinity if no dimensions are provided for ijk
42
58
  xMin = Math.max(0, xMin);
43
- xMax = Math.min(width - 1, xMax);
59
+ xMax = Math.min(Infinity, xMax);
44
60
  yMin = Math.max(0, yMin);
45
- yMax = Math.min(height - 1, yMax);
46
- zMin = Math.max(0, zMin);
47
- zMax = Math.min(depth - 1, zMax);
61
+ yMax = Math.min(Infinity, yMax);
62
+
63
+ if (is3D) {
64
+ zMin = Math.max(0, zMin);
65
+ zMax = Math.min(Infinity, zMax);
66
+ }
48
67
  }
49
68
 
50
- return [
51
- [xMin, xMax],
52
- [yMin, yMax],
53
- [zMin, zMax],
54
- ];
69
+ return is3D
70
+ ? [
71
+ [xMin, xMax],
72
+ [yMin, yMax],
73
+ [zMin, zMax],
74
+ ]
75
+ : [[xMin, xMax], [yMin, yMax], null];
55
76
  }
56
77
 
57
- export default getBoundingBoxAroundShape;
78
+ /**
79
+ * With a given vertices (points) coordinates in 2D or 3D in IJK, it calculates the minimum and maximum
80
+ * coordinate in each axis, and returns them. If clipBounds are provided it also
81
+ * clip the min, max to the provided width, height and depth
82
+ *
83
+ * @param points - shape corner points coordinates either in IJK (image coordinate)
84
+ * @param dimensions - bounds to clip the min, max
85
+ * @returns [[xMin,xMax],[yMin,yMax], [zMin,zMax]]
86
+ */
87
+ export function getBoundingBoxAroundShapeIJK(
88
+ points: Types.Point2[] | Types.Point3[],
89
+ dimensions?: Types.Point2 | Types.Point3
90
+ ): BoundingBox {
91
+ return calculateBoundingBox(points, dimensions, false);
92
+ }
93
+
94
+ /**
95
+ * With a given vertices (points) coordinates in 2D or 3D in World Coordinates, it calculates the minimum and maximum
96
+ * coordinate in each axis, and returns them. If clipBounds are provided it also
97
+ * clip the min, max to the provided width, height and depth
98
+ *
99
+ * @param points - shape corner points coordinates either in IJK (image coordinate)
100
+ * @param clipBounds - bounds to clip the min, max
101
+ * @returns [[xMin,xMax],[yMin,yMax], [zMin,zMax]]
102
+ */
103
+ export function getBoundingBoxAroundShapeWorld(
104
+ points: Types.Point2[] | Types.Point3[],
105
+ clipBounds?: Types.Point2 | Types.Point3
106
+ ): BoundingBox {
107
+ return calculateBoundingBox(points, clipBounds, true);
108
+ }
@@ -1,4 +1,13 @@
1
1
  import extend2DBoundingBoxInViewAxis from './extend2DBoundingBoxInViewAxis';
2
- import getBoundingBoxAroundShape from './getBoundingBoxAroundShape';
2
+ import {
3
+ getBoundingBoxAroundShapeIJK,
4
+ getBoundingBoxAroundShapeWorld,
5
+ } from './getBoundingBoxAroundShape';
3
6
 
4
- export { extend2DBoundingBoxInViewAxis, getBoundingBoxAroundShape };
7
+ export {
8
+ extend2DBoundingBoxInViewAxis,
9
+ getBoundingBoxAroundShapeIJK,
10
+ getBoundingBoxAroundShapeWorld,
11
+ // backwards compatibility
12
+ getBoundingBoxAroundShapeIJK as getBoundingBoxAroundShape,
13
+ };
@@ -3,12 +3,8 @@ import type { Types } from '@cornerstonejs/core';
3
3
 
4
4
  import type { vtkImageData } from '@kitware/vtk.js/Common/DataModel/ImageData';
5
5
  import { vec3 } from 'gl-matrix';
6
- import { pointInSphere } from './math/sphere';
7
- import pointInShapeCallback, {
8
- PointInShapeCallback,
9
- } from './pointInShapeCallback';
10
6
  import { BoundsIJK } from '../types';
11
- import { getBoundingBoxAroundShape } from './boundingBox';
7
+ import { getBoundingBoxAroundShapeIJK } from './boundingBox';
12
8
 
13
9
  const { transformWorldToIndex } = csUtils;
14
10
 
@@ -27,36 +23,7 @@ const { transformWorldToIndex } = csUtils;
27
23
  * @param circlePoints - bottom and top points of the great circle in world coordinates
28
24
  * @param callback - A callback function that will be called for each point in the shape.
29
25
  */
30
- export default function pointInSurroundingSphereCallback(
31
- imageData: vtkImageData,
32
- circlePoints: [Types.Point3, Types.Point3],
33
- callback: PointInShapeCallback,
34
- viewport?: Types.IVolumeViewport
35
- ): void {
36
- // We can run the sphere equation to determine if a point is inside
37
- // the sphere; however, since the imageData dimensions can be quite large, we
38
- // can narrow down the search by estimating the bounds of the sphere in index
39
- // space.
40
- const { boundsIJK, centerWorld, radiusWorld } = _getBounds(
41
- circlePoints,
42
- imageData,
43
- viewport
44
- );
45
-
46
- const sphereObj = {
47
- center: centerWorld,
48
- radius: radiusWorld,
49
- };
50
-
51
- pointInShapeCallback(
52
- imageData,
53
- (pointLPS) => pointInSphere(sphereObj, pointLPS),
54
- callback,
55
- boundsIJK
56
- );
57
- }
58
-
59
- function _getBounds(
26
+ function getSphereBoundsInfo(
60
27
  circlePoints: [Types.Point3, Types.Point3],
61
28
  imageData: vtkImageData,
62
29
  viewport
@@ -64,6 +31,8 @@ function _getBounds(
64
31
  boundsIJK: BoundsIJK;
65
32
  centerWorld: Types.Point3;
66
33
  radiusWorld: number;
34
+ topLeftWorld: Types.Point3;
35
+ bottomRightWorld: Types.Point3;
67
36
  } {
68
37
  const [bottom, top] = circlePoints;
69
38
 
@@ -77,53 +46,27 @@ function _getBounds(
77
46
  // sphere radius in world
78
47
  const radiusWorld = vec3.distance(bottom, top) / 2;
79
48
 
80
- let boundsIJK;
81
-
82
49
  if (!viewport) {
83
- // If no viewport is provide (no camera), we can estimate the bounding box
84
- // of the sphere in index space.
85
- // This is done by calculating the maximum value for radius in the index
86
- // space (since the radius is in world space, we need to convert it to index, and
87
- // each dimensions can have a different scale factor). Therefore, by finding
88
- // the minimum spacing value in the imageData, we can calculate the maximum
89
- // radius in index space and use that to calculate the bounds of the sphere
90
- // This will not be accurate, but it is a good first approximation.
91
- // sphere center in index
92
- const centerIJK = transformWorldToIndex(
93
- imageData,
94
- centerWorld as Types.Point3
50
+ throw new Error(
51
+ 'viewport is required in order to calculate the sphere bounds'
95
52
  );
96
-
97
- const spacings = imageData.getSpacing();
98
- const minSpacing = Math.min(...spacings);
99
-
100
- const maxRadiusIJK = Math.ceil(radiusWorld / minSpacing);
101
-
102
- boundsIJK = [
103
- [centerIJK[0] - maxRadiusIJK, centerIJK[0] + maxRadiusIJK],
104
- [centerIJK[1] - maxRadiusIJK, centerIJK[1] + maxRadiusIJK],
105
- [centerIJK[2] - maxRadiusIJK, centerIJK[2] + maxRadiusIJK],
106
- ];
107
-
108
- return {
109
- boundsIJK,
110
- centerWorld: centerWorld as Types.Point3,
111
- radiusWorld,
112
- };
113
53
  }
114
54
 
115
- boundsIJK = _computeBoundsIJKWithCamera(
116
- imageData,
117
- viewport,
118
- circlePoints,
119
- centerWorld,
120
- radiusWorld
121
- );
55
+ const { boundsIJK, topLeftWorld, bottomRightWorld } =
56
+ _computeBoundsIJKWithCamera(
57
+ imageData,
58
+ viewport,
59
+ circlePoints,
60
+ centerWorld,
61
+ radiusWorld
62
+ );
122
63
 
123
64
  return {
124
65
  boundsIJK,
125
66
  centerWorld: centerWorld as Types.Point3,
126
67
  radiusWorld,
68
+ topLeftWorld: topLeftWorld as Types.Point3,
69
+ bottomRightWorld: bottomRightWorld as Types.Point3,
127
70
  };
128
71
  }
129
72
 
@@ -137,6 +80,7 @@ function _computeBoundsIJKWithCamera(
137
80
  const [bottom, top] = circlePoints;
138
81
 
139
82
  const dimensions = imageData.getDimensions() as Types.Point3;
83
+
140
84
  const camera = viewport.getCamera();
141
85
 
142
86
  // Calculate viewRight from the camera, this will get used in order to
@@ -172,17 +116,30 @@ function _computeBoundsIJKWithCamera(
172
116
  vec3.scaleAndAdd(topLeftWorld, topLeftWorld, viewRight, -radiusWorld);
173
117
  vec3.scaleAndAdd(bottomRightWorld, bottomRightWorld, viewRight, radiusWorld);
174
118
 
175
- // convert the world coordinates to index coordinates
119
+ // In order to correctly come up with the boundsIJK, we need to consider
120
+ // all the points IJK to get the bounds, since the viewport might have
121
+ // rotate views and we cannot guarantee that the topLeft and bottomRight in the
122
+ // world, are the ones that will define the bounds in IJK
123
+ const topLeftIJK = transformWorldToIndex(
124
+ imageData,
125
+ topLeftWorld as Types.Point3
126
+ );
127
+ const bottomRightIJK = transformWorldToIndex(
128
+ imageData,
129
+ bottomRightWorld as Types.Point3
130
+ );
176
131
 
177
- const sphereCornersIJK = [
178
- <Types.Point3>transformWorldToIndex(imageData, <Types.Point3>topLeftWorld),
179
- <Types.Point3>(
180
- transformWorldToIndex(imageData, <Types.Point3>bottomRightWorld)
181
- ),
182
- ];
132
+ const pointsIJK = circlePoints.map((p) =>
133
+ transformWorldToIndex(imageData, p)
134
+ );
183
135
 
184
136
  // get the bounding box of the sphere in the image
185
- const boundsIJK = getBoundingBoxAroundShape(sphereCornersIJK, dimensions);
137
+ const boundsIJK = getBoundingBoxAroundShapeIJK(
138
+ [topLeftIJK, bottomRightIJK, ...pointsIJK],
139
+ dimensions
140
+ );
186
141
 
187
- return boundsIJK;
142
+ return { boundsIJK, topLeftWorld, bottomRightWorld };
188
143
  }
144
+
145
+ export { getSphereBoundsInfo };
@@ -21,7 +21,7 @@ import triggerAnnotationRender from './triggerAnnotationRender';
21
21
  import jumpToSlice from './viewport/jumpToSlice';
22
22
 
23
23
  import pointInShapeCallback from './pointInShapeCallback';
24
- import pointInSurroundingSphereCallback from './pointInSurroundingSphereCallback';
24
+ import { getSphereBoundsInfo } from './getSphereBoundsInfo';
25
25
  import scroll from './scroll';
26
26
  import { pointToString } from './pointToString';
27
27
  import annotationFrameRange from './annotationFrameRange';
@@ -71,7 +71,7 @@ export {
71
71
  triggerAnnotationRenderForViewportIds,
72
72
  triggerAnnotationRender,
73
73
  pointInShapeCallback,
74
- pointInSurroundingSphereCallback,
74
+ getSphereBoundsInfo,
75
75
  getAnnotationNearPoint,
76
76
  getAnnotationNearPointOnEnabledElement,
77
77
  jumpToSlice,
@@ -44,7 +44,7 @@ export default function pointInEllipse(
44
44
  * That is:
45
45
  *
46
46
  * ```
47
- * const inverts = precalcualtePointInEllipse(ellipse);
47
+ * const inverts = precalculatePointInEllipse(ellipse);
48
48
  * if( inverts.precalculated(pointLPS) ) ...
49
49
  * ```
50
50
  */
@@ -22,7 +22,7 @@ export type PointInShapeCallback = ({
22
22
  pointLPS: vec3;
23
23
  }) => void;
24
24
 
25
- export type ShapeFnCriteria = (pointIJK: vec3, pointLPS: vec3) => boolean;
25
+ export type ShapeFnCriteria = (pointLPS: vec3, pointIJK: vec3) => boolean;
26
26
 
27
27
  /**
28
28
  * For each point in the image (If boundsIJK is not provided, otherwise, for each
@@ -121,7 +121,7 @@ export default function pointInShapeCallback(
121
121
  const pointIJK: Types.Point3 = [i, j, k];
122
122
 
123
123
  // The current world position (pointLPS) is now in currentPos
124
- if (pointInShapeFn(currentPos as Types.Point3, currentPos)) {
124
+ if (pointInShapeFn(currentPos as Types.Point3, pointIJK)) {
125
125
  const index = k * zMultiple + j * yMultiple + i * xMultiple;
126
126
  let value;
127
127
  if (xMultiple > 2) {
@@ -1,6 +1,6 @@
1
1
  import { utilities as csUtils } from '@cornerstonejs/core';
2
2
  import type { Types } from '@cornerstonejs/core';
3
- import getBoundingBoxAroundShape from '../boundingBox/getBoundingBoxAroundShape';
3
+ import { getBoundingBoxAroundShapeIJK } from '../boundingBox/getBoundingBoxAroundShape';
4
4
  import extend2DBoundingBoxInViewAxis from '../boundingBox/extend2DBoundingBoxInViewAxis';
5
5
 
6
6
  type Options = {
@@ -29,7 +29,10 @@ function getBoundsIJKFromRectangleAnnotations(
29
29
  const rectangleCornersIJK = pointsToUse.map(
30
30
  (world) => csUtils.transformWorldToIndex(imageData, world) as Types.Point3
31
31
  );
32
- let boundsIJK = getBoundingBoxAroundShape(rectangleCornersIJK, dimensions);
32
+ let boundsIJK = getBoundingBoxAroundShapeIJK(
33
+ rectangleCornersIJK,
34
+ dimensions
35
+ );
33
36
 
34
37
  // If the tool is 2D but it is configured to project to X amount of slices
35
38
  // Don't project the slices if projectionPoints have been used to define the extents
@@ -1,3 +1,4 @@
1
1
  import getBoundsIJKFromRectangleAnnotations from './getBoundsIJKFromRectangleAnnotations';
2
+ import { isAxisAlignedRectangle } from './isAxisAlignedRectangle';
2
3
 
3
- export { getBoundsIJKFromRectangleAnnotations };
4
+ export { getBoundsIJKFromRectangleAnnotations, isAxisAlignedRectangle };