@cornerstonejs/tools 0.56.2 → 0.56.4

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 (352) hide show
  1. package/package.json +5 -4
  2. package/src/constants/COLOR_LUT.ts +262 -0
  3. package/src/constants/index.ts +3 -0
  4. package/src/cursors/ImageMouseCursor.ts +39 -0
  5. package/src/cursors/MouseCursor.ts +114 -0
  6. package/src/cursors/SVGCursorDescriptor.ts +462 -0
  7. package/src/cursors/SVGMouseCursor.ts +145 -0
  8. package/src/cursors/elementCursor.ts +69 -0
  9. package/src/cursors/index.ts +24 -0
  10. package/src/cursors/setCursorForElement.ts +33 -0
  11. package/src/drawingSvg/_getHash.ts +9 -0
  12. package/src/drawingSvg/_setAttributesIfNecessary.ts +13 -0
  13. package/src/drawingSvg/_setNewAttributesIfValid.ts +10 -0
  14. package/src/drawingSvg/clearByToolType.ts +26 -0
  15. package/src/drawingSvg/draw.ts +16 -0
  16. package/src/drawingSvg/drawArrow.ts +82 -0
  17. package/src/drawingSvg/drawCircle.ts +62 -0
  18. package/src/drawingSvg/drawEllipse.ts +71 -0
  19. package/src/drawingSvg/drawHandles.ts +87 -0
  20. package/src/drawingSvg/drawLine.ts +70 -0
  21. package/src/drawingSvg/drawLink.ts +76 -0
  22. package/src/drawingSvg/drawLinkedTextBox.ts +64 -0
  23. package/src/drawingSvg/drawPolyline.ts +80 -0
  24. package/src/drawingSvg/drawRect.ts +70 -0
  25. package/src/drawingSvg/drawTextBox.ts +213 -0
  26. package/src/drawingSvg/getSvgDrawingHelper.ts +98 -0
  27. package/src/drawingSvg/index.ts +23 -0
  28. package/src/enums/AnnotationStyleStates.ts +22 -0
  29. package/src/enums/Events.ts +242 -0
  30. package/src/enums/SegmentationRepresentations.ts +12 -0
  31. package/src/enums/ToolBindings.ts +37 -0
  32. package/src/enums/ToolModes.ts +31 -0
  33. package/src/enums/Touch.ts +8 -0
  34. package/src/enums/index.js +16 -0
  35. package/src/eventDispatchers/annotationModifiedEventDispatcher.ts +41 -0
  36. package/src/eventDispatchers/cameraModifiedEventDispatcher.ts +41 -0
  37. package/src/eventDispatchers/imageRenderedEventDispatcher.ts +37 -0
  38. package/src/eventDispatchers/imageSpacingCalibratedEventDispatcher.ts +50 -0
  39. package/src/eventDispatchers/index.js +15 -0
  40. package/src/eventDispatchers/keyboardEventHandlers/index.js +4 -0
  41. package/src/eventDispatchers/keyboardEventHandlers/keyDown.ts +29 -0
  42. package/src/eventDispatchers/keyboardEventHandlers/keyUp.ts +33 -0
  43. package/src/eventDispatchers/keyboardToolEventDispatcher.ts +28 -0
  44. package/src/eventDispatchers/mouseEventHandlers/index.js +19 -0
  45. package/src/eventDispatchers/mouseEventHandlers/mouseClick.ts +13 -0
  46. package/src/eventDispatchers/mouseEventHandlers/mouseDoubleClick.ts +13 -0
  47. package/src/eventDispatchers/mouseEventHandlers/mouseDown.ts +196 -0
  48. package/src/eventDispatchers/mouseEventHandlers/mouseDownActivate.ts +35 -0
  49. package/src/eventDispatchers/mouseEventHandlers/mouseDrag.ts +25 -0
  50. package/src/eventDispatchers/mouseEventHandlers/mouseMove.ts +70 -0
  51. package/src/eventDispatchers/mouseEventHandlers/mouseUp.ts +9 -0
  52. package/src/eventDispatchers/mouseEventHandlers/mouseWheel.ts +13 -0
  53. package/src/eventDispatchers/mouseToolEventDispatcher.ts +64 -0
  54. package/src/eventDispatchers/shared/customCallbackHandler.ts +73 -0
  55. package/src/eventDispatchers/shared/getActiveToolForKeyboardEvent.ts +58 -0
  56. package/src/eventDispatchers/shared/getActiveToolForMouseEvent.ts +61 -0
  57. package/src/eventDispatchers/shared/getActiveToolForTouchEvent.ts +64 -0
  58. package/src/eventDispatchers/shared/getMouseModifier.ts +30 -0
  59. package/src/eventDispatchers/shared/getToolsWithModesForMouseEvent.ts +56 -0
  60. package/src/eventDispatchers/shared/getToolsWithModesForTouchEvent.ts +54 -0
  61. package/src/eventDispatchers/touchEventHandlers/index.js +15 -0
  62. package/src/eventDispatchers/touchEventHandlers/touchDrag.ts +23 -0
  63. package/src/eventDispatchers/touchEventHandlers/touchEnd.ts +9 -0
  64. package/src/eventDispatchers/touchEventHandlers/touchPress.ts +13 -0
  65. package/src/eventDispatchers/touchEventHandlers/touchStart.ts +174 -0
  66. package/src/eventDispatchers/touchEventHandlers/touchStartActivate.ts +36 -0
  67. package/src/eventDispatchers/touchEventHandlers/touchTap.ts +9 -0
  68. package/src/eventDispatchers/touchToolEventDispatcher.ts +51 -0
  69. package/src/eventListeners/annotations/annotationModifiedListener.ts +22 -0
  70. package/src/eventListeners/annotations/annotationSelectionListener.ts +29 -0
  71. package/src/eventListeners/annotations/index.ts +4 -0
  72. package/src/eventListeners/index.ts +28 -0
  73. package/src/eventListeners/keyboard/index.ts +16 -0
  74. package/src/eventListeners/keyboard/keyDownListener.ts +99 -0
  75. package/src/eventListeners/mouse/getMouseEventPoints.ts +66 -0
  76. package/src/eventListeners/mouse/index.ts +55 -0
  77. package/src/eventListeners/mouse/mouseDoubleClickListener.ts +55 -0
  78. package/src/eventListeners/mouse/mouseDownListener.ts +519 -0
  79. package/src/eventListeners/mouse/mouseMoveListener.ts +33 -0
  80. package/src/eventListeners/segmentation/index.ts +11 -0
  81. package/src/eventListeners/segmentation/segmentationDataModifiedEventListener.ts +61 -0
  82. package/src/eventListeners/segmentation/segmentationModifiedEventListener.ts +32 -0
  83. package/src/eventListeners/segmentation/segmentationRepresentationModifiedEventListener.ts +15 -0
  84. package/src/eventListeners/segmentation/segmentationRepresentationRemovedEventListener.ts +16 -0
  85. package/src/eventListeners/touch/getTouchEventPoints.ts +75 -0
  86. package/src/eventListeners/touch/index.ts +37 -0
  87. package/src/eventListeners/touch/preventGhostClick.js +72 -0
  88. package/src/eventListeners/touch/touchStartListener.ts +499 -0
  89. package/src/eventListeners/wheel/index.ts +27 -0
  90. package/src/eventListeners/wheel/normalizeWheel.ts +69 -0
  91. package/src/eventListeners/wheel/wheelListener.ts +51 -0
  92. package/src/index.ts +133 -0
  93. package/src/init.ts +187 -0
  94. package/src/stateManagement/annotation/FrameOfReferenceSpecificAnnotationManager.ts +399 -0
  95. package/src/stateManagement/annotation/annotationLocking.ts +178 -0
  96. package/src/stateManagement/annotation/annotationSelection.ts +163 -0
  97. package/src/stateManagement/annotation/annotationState.ts +180 -0
  98. package/src/stateManagement/annotation/annotationVisibility.ts +156 -0
  99. package/src/stateManagement/annotation/config/ToolStyle.ts +265 -0
  100. package/src/stateManagement/annotation/config/getFont.ts +36 -0
  101. package/src/stateManagement/annotation/config/getState.ts +26 -0
  102. package/src/stateManagement/annotation/config/helpers.ts +55 -0
  103. package/src/stateManagement/annotation/config/index.ts +5 -0
  104. package/src/stateManagement/annotation/helpers/state.ts +83 -0
  105. package/src/stateManagement/annotation/index.ts +15 -0
  106. package/src/stateManagement/index.js +40 -0
  107. package/src/stateManagement/segmentation/SegmentationStateManager.ts +491 -0
  108. package/src/stateManagement/segmentation/activeSegmentation.ts +60 -0
  109. package/src/stateManagement/segmentation/addSegmentationRepresentations.ts +77 -0
  110. package/src/stateManagement/segmentation/addSegmentations.ts +27 -0
  111. package/src/stateManagement/segmentation/config/index.ts +29 -0
  112. package/src/stateManagement/segmentation/config/segmentationColor.ts +132 -0
  113. package/src/stateManagement/segmentation/config/segmentationConfig.ts +195 -0
  114. package/src/stateManagement/segmentation/config/segmentationVisibility.ts +171 -0
  115. package/src/stateManagement/segmentation/helpers/index.ts +3 -0
  116. package/src/stateManagement/segmentation/helpers/normalizeSegmentationInput.ts +35 -0
  117. package/src/stateManagement/segmentation/helpers/validateSegmentationInput.ts +41 -0
  118. package/src/stateManagement/segmentation/index.ts +22 -0
  119. package/src/stateManagement/segmentation/removeSegmentationsFromToolGroup.ts +85 -0
  120. package/src/stateManagement/segmentation/segmentIndex.ts +38 -0
  121. package/src/stateManagement/segmentation/segmentLocking.ts +72 -0
  122. package/src/stateManagement/segmentation/segmentationState.ts +429 -0
  123. package/src/stateManagement/segmentation/triggerSegmentationEvents.ts +157 -0
  124. package/src/store/SynchronizerManager/Synchronizer.ts +344 -0
  125. package/src/store/SynchronizerManager/createSynchronizer.ts +41 -0
  126. package/src/store/SynchronizerManager/destroy.ts +14 -0
  127. package/src/store/SynchronizerManager/destroySynchronizer.ts +25 -0
  128. package/src/store/SynchronizerManager/getAllSynchronizers.ts +12 -0
  129. package/src/store/SynchronizerManager/getSynchronizer.ts +13 -0
  130. package/src/store/SynchronizerManager/getSynchronizersForViewport.ts +44 -0
  131. package/src/store/SynchronizerManager/index.js +15 -0
  132. package/src/store/ToolGroupManager/ToolGroup.ts +679 -0
  133. package/src/store/ToolGroupManager/createToolGroup.ts +33 -0
  134. package/src/store/ToolGroupManager/destroy.ts +24 -0
  135. package/src/store/ToolGroupManager/destroyToolGroup.ts +26 -0
  136. package/src/store/ToolGroupManager/getAllToolGroups.ts +12 -0
  137. package/src/store/ToolGroupManager/getToolGroup.ts +14 -0
  138. package/src/store/ToolGroupManager/getToolGroupForViewport.ts +44 -0
  139. package/src/store/ToolGroupManager/getToolGroupsWithToolName.ts +33 -0
  140. package/src/store/ToolGroupManager/index.ts +17 -0
  141. package/src/store/addEnabledElement.ts +137 -0
  142. package/src/store/addTool.ts +56 -0
  143. package/src/store/cancelActiveManipulations.ts +30 -0
  144. package/src/store/filterMoveableAnnotationTools.ts +61 -0
  145. package/src/store/filterToolsWithAnnotationsForElement.ts +51 -0
  146. package/src/store/filterToolsWithMoveableHandles.ts +51 -0
  147. package/src/store/index.ts +29 -0
  148. package/src/store/removeEnabledElement.ts +132 -0
  149. package/src/store/state.ts +57 -0
  150. package/src/store/svgNodeCache.ts +7 -0
  151. package/src/synchronizers/callbacks/areViewportsCoplanar .ts +12 -0
  152. package/src/synchronizers/callbacks/cameraSyncCallback.ts +33 -0
  153. package/src/synchronizers/callbacks/stackImageSyncCallback.ts +157 -0
  154. package/src/synchronizers/callbacks/voiSyncCallback.ts +51 -0
  155. package/src/synchronizers/callbacks/zoomPanSyncCallback.ts +43 -0
  156. package/src/synchronizers/index.ts +11 -0
  157. package/src/synchronizers/synchronizers/createCameraPositionSynchronizer.ts +25 -0
  158. package/src/synchronizers/synchronizers/createStackImageSynchronizer.ts +25 -0
  159. package/src/synchronizers/synchronizers/createVOISynchronizer.ts +24 -0
  160. package/src/synchronizers/synchronizers/createZoomPanSynchronizer.ts +25 -0
  161. package/src/synchronizers/synchronizers/index.ts +11 -0
  162. package/src/tools/CrosshairsTool.ts +2693 -0
  163. package/src/tools/MIPJumpToClickTool.ts +99 -0
  164. package/src/tools/MagnifyTool.ts +319 -0
  165. package/src/tools/PanTool.ts +58 -0
  166. package/src/tools/PlanarRotateTool.ts +77 -0
  167. package/src/tools/ReferenceCursors.ts +466 -0
  168. package/src/tools/ReferenceLinesTool.ts +279 -0
  169. package/src/tools/ScaleOverlayTool.ts +685 -0
  170. package/src/tools/StackScrollTool.ts +97 -0
  171. package/src/tools/StackScrollToolMouseWheelTool.ts +58 -0
  172. package/src/tools/TrackballRotateTool.ts +141 -0
  173. package/src/tools/VolumeRotateMouseWheelTool.ts +86 -0
  174. package/src/tools/WindowLevelTool.ts +260 -0
  175. package/src/tools/ZoomTool.ts +293 -0
  176. package/src/tools/annotation/AngleTool.ts +835 -0
  177. package/src/tools/annotation/ArrowAnnotateTool.ts +820 -0
  178. package/src/tools/annotation/BidirectionalTool.ts +1350 -0
  179. package/src/tools/annotation/CircleROITool.ts +1070 -0
  180. package/src/tools/annotation/CobbAngleTool.ts +815 -0
  181. package/src/tools/annotation/DragProbeTool.ts +213 -0
  182. package/src/tools/annotation/EllipticalROITool.ts +1223 -0
  183. package/src/tools/annotation/LengthTool.ts +861 -0
  184. package/src/tools/annotation/PlanarFreehandROITool.ts +636 -0
  185. package/src/tools/annotation/ProbeTool.ts +681 -0
  186. package/src/tools/annotation/RectangleROITool.ts +1028 -0
  187. package/src/tools/annotation/planarFreehandROITool/closedContourEditLoop.ts +488 -0
  188. package/src/tools/annotation/planarFreehandROITool/drawLoop.ts +462 -0
  189. package/src/tools/annotation/planarFreehandROITool/editLoopCommon.ts +331 -0
  190. package/src/tools/annotation/planarFreehandROITool/findOpenUShapedContourVectorToPeak.ts +74 -0
  191. package/src/tools/annotation/planarFreehandROITool/openContourEditLoop.ts +612 -0
  192. package/src/tools/annotation/planarFreehandROITool/openContourEndEditLoop.ts +74 -0
  193. package/src/tools/annotation/planarFreehandROITool/renderMethods.ts +407 -0
  194. package/src/tools/base/AnnotationDisplayTool.ts +228 -0
  195. package/src/tools/base/AnnotationTool.ts +307 -0
  196. package/src/tools/base/BaseTool.ts +215 -0
  197. package/src/tools/base/index.ts +4 -0
  198. package/src/tools/displayTools/Contour/addContourToElement.ts +135 -0
  199. package/src/tools/displayTools/Contour/contourDisplay.ts +252 -0
  200. package/src/tools/displayTools/Contour/index.ts +3 -0
  201. package/src/tools/displayTools/Contour/removeContourFromElement.ts +35 -0
  202. package/src/tools/displayTools/Labelmap/addLabelmapToElement.ts +57 -0
  203. package/src/tools/displayTools/Labelmap/index.ts +4 -0
  204. package/src/tools/displayTools/Labelmap/labelmapConfig.ts +37 -0
  205. package/src/tools/displayTools/Labelmap/labelmapDisplay.ts +461 -0
  206. package/src/tools/displayTools/Labelmap/removeLabelmapFromElement.ts +27 -0
  207. package/src/tools/displayTools/Labelmap/validateRepresentationData.ts +30 -0
  208. package/src/tools/displayTools/SegmentationDisplayTool.ts +198 -0
  209. package/src/tools/index.ts +84 -0
  210. package/src/tools/segmentation/BrushTool.ts +474 -0
  211. package/src/tools/segmentation/CircleScissorsTool.ts +365 -0
  212. package/src/tools/segmentation/PaintFillTool.ts +370 -0
  213. package/src/tools/segmentation/RectangleROIStartEndThresholdTool.ts +471 -0
  214. package/src/tools/segmentation/RectangleROIThresholdTool.ts +281 -0
  215. package/src/tools/segmentation/RectangleScissorsTool.ts +382 -0
  216. package/src/tools/segmentation/SphereScissorsTool.ts +368 -0
  217. package/src/tools/segmentation/strategies/eraseCircle.ts +30 -0
  218. package/src/tools/segmentation/strategies/eraseRectangle.ts +81 -0
  219. package/src/tools/segmentation/strategies/eraseSphere.ts +27 -0
  220. package/src/tools/segmentation/strategies/fillCircle.ts +185 -0
  221. package/src/tools/segmentation/strategies/fillRectangle.ts +110 -0
  222. package/src/tools/segmentation/strategies/fillSphere.ts +88 -0
  223. package/src/tools/segmentation/strategies/index.ts +9 -0
  224. package/src/types/AnnotationGroupSelector.ts +7 -0
  225. package/src/types/AnnotationStyle.ts +42 -0
  226. package/src/types/AnnotationTypes.ts +109 -0
  227. package/src/types/BoundsIJK.ts +5 -0
  228. package/src/types/CINETypes.ts +32 -0
  229. package/src/types/ContourTypes.ts +26 -0
  230. package/src/types/CursorTypes.ts +12 -0
  231. package/src/types/EventTypes.ts +657 -0
  232. package/src/types/FloodFillTypes.ts +19 -0
  233. package/src/types/IAnnotationManager.ts +89 -0
  234. package/src/types/IDistance.ts +16 -0
  235. package/src/types/IPoints.ts +18 -0
  236. package/src/types/ISetToolModeOptions.ts +29 -0
  237. package/src/types/ISynchronizerEventHandler.ts +11 -0
  238. package/src/types/IToolClassReference.ts +5 -0
  239. package/src/types/IToolGroup.ts +72 -0
  240. package/src/types/ITouchPoints.ts +14 -0
  241. package/src/types/InteractionTypes.ts +6 -0
  242. package/src/types/InternalToolTypes.ts +19 -0
  243. package/src/types/JumpToSliceOptions.ts +7 -0
  244. package/src/types/LabelmapTypes.ts +41 -0
  245. package/src/types/PlanarBoundingBox.ts +8 -0
  246. package/src/types/SVGDrawingHelper.ts +10 -0
  247. package/src/types/ScrollOptions.ts +9 -0
  248. package/src/types/SegmentationStateTypes.ts +248 -0
  249. package/src/types/ToolHandle.ts +26 -0
  250. package/src/types/ToolProps.ts +16 -0
  251. package/src/types/ToolSpecificAnnotationTypes.ts +311 -0
  252. package/src/types/index.ts +115 -0
  253. package/src/utilities/boundingBox/extend2DBoundingBoxInViewAxis.ts +29 -0
  254. package/src/utilities/boundingBox/getBoundingBoxAroundShape.ts +57 -0
  255. package/src/utilities/boundingBox/index.ts +4 -0
  256. package/src/utilities/calibrateImageSpacing.ts +46 -0
  257. package/src/utilities/cine/events.ts +9 -0
  258. package/src/utilities/cine/index.ts +5 -0
  259. package/src/utilities/cine/playClip.ts +435 -0
  260. package/src/utilities/cine/state.ts +18 -0
  261. package/src/utilities/clip.js +30 -0
  262. package/src/utilities/debounce.js +217 -0
  263. package/src/utilities/drawing/getTextBoxCoordsCanvas.ts +45 -0
  264. package/src/utilities/drawing/index.ts +3 -0
  265. package/src/utilities/dynamicVolume/getDataInTime.ts +110 -0
  266. package/src/utilities/dynamicVolume/index.ts +2 -0
  267. package/src/utilities/getAnnotationNearPoint.ts +130 -0
  268. package/src/utilities/getModalityUnit.ts +11 -0
  269. package/src/utilities/getToolsWithModesForElement.ts +52 -0
  270. package/src/utilities/index.ts +68 -0
  271. package/src/utilities/isObject.js +29 -0
  272. package/src/utilities/math/angle/angleBetweenLines.ts +29 -0
  273. package/src/utilities/math/circle/_types.ts +6 -0
  274. package/src/utilities/math/circle/getCanvasCircleCorners.ts +23 -0
  275. package/src/utilities/math/circle/getCanvasCircleRadius.ts +16 -0
  276. package/src/utilities/math/circle/index.ts +4 -0
  277. package/src/utilities/math/ellipse/getCanvasEllipseCorners.ts +26 -0
  278. package/src/utilities/math/ellipse/index.ts +4 -0
  279. package/src/utilities/math/ellipse/pointInEllipse.ts +38 -0
  280. package/src/utilities/math/ellipse/pointInEllipsoidWithConstraint.ts +35 -0
  281. package/src/utilities/math/index.ts +8 -0
  282. package/src/utilities/math/line/distanceToPoint.ts +24 -0
  283. package/src/utilities/math/line/distanceToPointSquared.ts +44 -0
  284. package/src/utilities/math/line/index.ts +5 -0
  285. package/src/utilities/math/line/intersectLine.ts +92 -0
  286. package/src/utilities/math/midPoint.ts +24 -0
  287. package/src/utilities/math/point/distanceToPoint.ts +22 -0
  288. package/src/utilities/math/point/index.ts +3 -0
  289. package/src/utilities/math/polyline/addCanvasPointsToArray.ts +62 -0
  290. package/src/utilities/math/polyline/calculateAreaOfPoints.ts +23 -0
  291. package/src/utilities/math/polyline/getIntersectionWithPolyline.ts +182 -0
  292. package/src/utilities/math/polyline/getSubPixelSpacingAndXYDirections.ts +99 -0
  293. package/src/utilities/math/polyline/index.ts +19 -0
  294. package/src/utilities/math/polyline/planarFreehandROIInternalTypes.ts +36 -0
  295. package/src/utilities/math/polyline/pointCanProjectOnLine.ts +57 -0
  296. package/src/utilities/math/polyline/pointsAreWithinCloseContourProximity.ts +15 -0
  297. package/src/utilities/math/rectangle/distanceToPoint.ts +82 -0
  298. package/src/utilities/math/rectangle/index.ts +3 -0
  299. package/src/utilities/math/sphere/index.ts +3 -0
  300. package/src/utilities/math/sphere/pointInSphere.ts +31 -0
  301. package/src/utilities/math/vec2/findClosestPoint.ts +40 -0
  302. package/src/utilities/math/vec2/index.ts +4 -0
  303. package/src/utilities/math/vec2/liangBarksyClip.ts +84 -0
  304. package/src/utilities/orientation/getOrientationStringLPS.ts +52 -0
  305. package/src/utilities/orientation/index.ts +4 -0
  306. package/src/utilities/orientation/invertOrientationStringLPS.ts +21 -0
  307. package/src/utilities/planar/filterAnnotationsForDisplay.ts +68 -0
  308. package/src/utilities/planar/filterAnnotationsWithinSlice.ts +85 -0
  309. package/src/utilities/planar/getPointInLineOfSightWithCriteria.ts +104 -0
  310. package/src/utilities/planar/getWorldWidthAndHeightFromCorners.ts +51 -0
  311. package/src/utilities/planar/getWorldWidthAndHeightFromTwoPoints.ts +51 -0
  312. package/src/utilities/planar/index.ts +18 -0
  313. package/src/utilities/planarFreehandROITool/index.ts +7 -0
  314. package/src/utilities/planarFreehandROITool/interpolateAnnotation.ts +87 -0
  315. package/src/utilities/planarFreehandROITool/interpolatePoints.ts +214 -0
  316. package/src/utilities/planarFreehandROITool/interpolation/algorithms/bspline.ts +55 -0
  317. package/src/utilities/planarFreehandROITool/interpolation/interpolateSegmentPoints.ts +90 -0
  318. package/src/utilities/pointInShapeCallback.ts +138 -0
  319. package/src/utilities/pointInSurroundingSphereCallback.ts +188 -0
  320. package/src/utilities/rectangleROITool/getBoundsIJKFromRectangleAnnotations.ts +76 -0
  321. package/src/utilities/rectangleROITool/index.ts +3 -0
  322. package/src/utilities/scroll.ts +62 -0
  323. package/src/utilities/segmentation/brushSizeForToolGroup.ts +72 -0
  324. package/src/utilities/segmentation/brushThresholdForToolGroup.ts +65 -0
  325. package/src/utilities/segmentation/createLabelmapVolumeForViewport.ts +74 -0
  326. package/src/utilities/segmentation/createMergedLabelmapForIndex.ts +65 -0
  327. package/src/utilities/segmentation/floodFill.ts +194 -0
  328. package/src/utilities/segmentation/getDefaultRepresentationConfig.ts +20 -0
  329. package/src/utilities/segmentation/index.ts +33 -0
  330. package/src/utilities/segmentation/isValidRepresentationConfig.ts +22 -0
  331. package/src/utilities/segmentation/rectangleROIThresholdVolumeByRange.ts +91 -0
  332. package/src/utilities/segmentation/thresholdSegmentationByRange.ts +129 -0
  333. package/src/utilities/segmentation/thresholdVolumeByRange.ts +150 -0
  334. package/src/utilities/segmentation/triggerSegmentationRender.ts +206 -0
  335. package/src/utilities/segmentation/utilities.ts +116 -0
  336. package/src/utilities/stackPrefetch/index.ts +8 -0
  337. package/src/utilities/stackPrefetch/stackPrefetch.ts +405 -0
  338. package/src/utilities/stackPrefetch/state.ts +17 -0
  339. package/src/utilities/throttle.js +69 -0
  340. package/src/utilities/touch/index.ts +246 -0
  341. package/src/utilities/triggerAnnotationRender.ts +237 -0
  342. package/src/utilities/triggerAnnotationRenderForViewportIds.ts +18 -0
  343. package/src/utilities/viewport/index.ts +5 -0
  344. package/src/utilities/viewport/isViewportPreScaled.ts +24 -0
  345. package/src/utilities/viewport/jumpToSlice.ts +73 -0
  346. package/src/utilities/viewport/jumpToWorld.ts +58 -0
  347. package/src/utilities/viewportFilters/filterViewportsWithFrameOfReferenceUID.ts +28 -0
  348. package/src/utilities/viewportFilters/filterViewportsWithParallelNormals.ts +26 -0
  349. package/src/utilities/viewportFilters/filterViewportsWithSameOrientation.ts +15 -0
  350. package/src/utilities/viewportFilters/filterViewportsWithToolEnabled.ts +72 -0
  351. package/src/utilities/viewportFilters/getViewportIdsWithToolToRender.ts +45 -0
  352. package/src/utilities/viewportFilters/index.ts +11 -0
@@ -0,0 +1,1028 @@
1
+ import { AnnotationTool } from '../base';
2
+
3
+ import {
4
+ getEnabledElement,
5
+ VolumeViewport,
6
+ triggerEvent,
7
+ eventTarget,
8
+ utilities as csUtils,
9
+ } from '@cornerstonejs/core';
10
+ import type { Types } from '@cornerstonejs/core';
11
+
12
+ import throttle from '../../utilities/throttle';
13
+ import {
14
+ addAnnotation,
15
+ getAnnotations,
16
+ removeAnnotation,
17
+ } from '../../stateManagement';
18
+ import { isAnnotationLocked } from '../../stateManagement/annotation/annotationLocking';
19
+ import { isAnnotationVisible } from '../../stateManagement/annotation/annotationVisibility';
20
+ import {
21
+ drawHandles as drawHandlesSvg,
22
+ drawLinkedTextBox as drawLinkedTextBoxSvg,
23
+ drawRect as drawRectSvg,
24
+ } from '../../drawingSvg';
25
+ import { state } from '../../store';
26
+ import { Events } from '../../enums';
27
+ import { getViewportIdsWithToolToRender } from '../../utilities/viewportFilters';
28
+ import * as rectangle from '../../utilities/math/rectangle';
29
+ import { getTextBoxCoordsCanvas } from '../../utilities/drawing';
30
+ import getWorldWidthAndHeightFromCorners from '../../utilities/planar/getWorldWidthAndHeightFromCorners';
31
+ import {
32
+ resetElementCursor,
33
+ hideElementCursor,
34
+ } from '../../cursors/elementCursor';
35
+ import triggerAnnotationRenderForViewportIds from '../../utilities/triggerAnnotationRenderForViewportIds';
36
+
37
+ import {
38
+ EventTypes,
39
+ ToolHandle,
40
+ TextBoxHandle,
41
+ ToolProps,
42
+ PublicToolProps,
43
+ InteractionTypes,
44
+ SVGDrawingHelper,
45
+ } from '../../types';
46
+ import { RectangleROIAnnotation } from '../../types/ToolSpecificAnnotationTypes';
47
+ import {
48
+ AnnotationCompletedEventDetail,
49
+ AnnotationModifiedEventDetail,
50
+ } from '../../types/EventTypes';
51
+ import { StyleSpecifier } from '../../types/AnnotationStyle';
52
+ import { getModalityUnit } from '../../utilities/getModalityUnit';
53
+ import { isViewportPreScaled } from '../../utilities/viewport/isViewportPreScaled';
54
+
55
+ const { transformWorldToIndex } = csUtils;
56
+
57
+ /**
58
+ * RectangleROIAnnotation let you draw annotations that measures the statistics
59
+ * such as area, max, mean and stdDev of a Rectangular region of interest.
60
+ * You can use RectangleROIAnnotation in all perpendicular views (axial, sagittal, coronal).
61
+ * Note: annotation tools in cornerstone3DTools exists in the exact location
62
+ * in the physical 3d space, as a result, by default, all annotations that are
63
+ * drawing in the same frameOfReference will get shared between viewports that
64
+ * are in the same frameOfReference. RectangleROI tool's text box lines are dynamically
65
+ * generated based on the viewport's underlying Modality. For instance, if
66
+ * the viewport is displaying CT, the text box will shown the statistics in Hounsfield units,
67
+ * and if the viewport is displaying PET, the text box will show the statistics in
68
+ * SUV units.
69
+ *
70
+ * The resulting annotation's data (statistics) and metadata (the
71
+ * state of the viewport while drawing was happening) will get added to the
72
+ * ToolState manager and can be accessed from the ToolState by calling getAnnotations
73
+ * or similar methods.
74
+ *
75
+ * ```js
76
+ * cornerstoneTools.addTool(RectangleROITool)
77
+ *
78
+ * const toolGroup = ToolGroupManager.createToolGroup('toolGroupId')
79
+ *
80
+ * toolGroup.addTool(RectangleROITool.toolName)
81
+ *
82
+ * toolGroup.addViewport('viewportId', 'renderingEngineId')
83
+ *
84
+ * toolGroup.setToolActive(RectangleROITool.toolName, {
85
+ * bindings: [
86
+ * {
87
+ * mouseButton: MouseBindings.Primary, // Left Click
88
+ * },
89
+ * ],
90
+ * })
91
+ * ```
92
+ *
93
+ * Read more in the Docs section of the website.
94
+ */
95
+ class RectangleROITool extends AnnotationTool {
96
+ static toolName;
97
+
98
+ _throttledCalculateCachedStats: any;
99
+ editData: {
100
+ annotation: any;
101
+ viewportIdsToRender: string[];
102
+ handleIndex?: number;
103
+ movingTextBox?: boolean;
104
+ newAnnotation?: boolean;
105
+ hasMoved?: boolean;
106
+ } | null;
107
+ isDrawing: boolean;
108
+ isHandleOutsideImage: boolean;
109
+
110
+ constructor(
111
+ toolProps: PublicToolProps = {},
112
+ defaultToolProps: ToolProps = {
113
+ supportedInteractionTypes: ['Mouse', 'Touch'],
114
+ configuration: {
115
+ shadow: true,
116
+ preventHandleOutsideImage: false,
117
+ },
118
+ }
119
+ ) {
120
+ super(toolProps, defaultToolProps);
121
+
122
+ this._throttledCalculateCachedStats = throttle(
123
+ this._calculateCachedStats,
124
+ 100,
125
+ { trailing: true }
126
+ );
127
+ }
128
+
129
+ /**
130
+ * Based on the current position of the mouse and the current imageId to create
131
+ * a RectangleROI Annotation and stores it in the annotationManager
132
+ *
133
+ * @param evt - EventTypes.NormalizedMouseEventType
134
+ * @returns The annotation object.
135
+ *
136
+ */
137
+ addNewAnnotation = (
138
+ evt: EventTypes.InteractionEventType
139
+ ): RectangleROIAnnotation => {
140
+ const eventDetail = evt.detail;
141
+ const { currentPoints, element } = eventDetail;
142
+ const worldPos = currentPoints.world;
143
+
144
+ const enabledElement = getEnabledElement(element);
145
+ const { viewport, renderingEngine } = enabledElement;
146
+
147
+ this.isDrawing = true;
148
+
149
+ const camera = viewport.getCamera();
150
+ const { viewPlaneNormal, viewUp } = camera;
151
+
152
+ const referencedImageId = this.getReferencedImageId(
153
+ viewport,
154
+ worldPos,
155
+ viewPlaneNormal,
156
+ viewUp
157
+ );
158
+
159
+ const FrameOfReferenceUID = viewport.getFrameOfReferenceUID();
160
+
161
+ const annotation = {
162
+ invalidated: true,
163
+ highlighted: true,
164
+ metadata: {
165
+ toolName: this.getToolName(),
166
+ viewPlaneNormal: <Types.Point3>[...viewPlaneNormal],
167
+ viewUp: <Types.Point3>[...viewUp],
168
+ FrameOfReferenceUID,
169
+ referencedImageId,
170
+ },
171
+ data: {
172
+ label: '',
173
+ handles: {
174
+ points: [
175
+ <Types.Point3>[...worldPos],
176
+ <Types.Point3>[...worldPos],
177
+ <Types.Point3>[...worldPos],
178
+ <Types.Point3>[...worldPos],
179
+ ],
180
+ textBox: {
181
+ hasMoved: false,
182
+ worldPosition: <Types.Point3>[0, 0, 0],
183
+ worldBoundingBox: {
184
+ topLeft: <Types.Point3>[0, 0, 0],
185
+ topRight: <Types.Point3>[0, 0, 0],
186
+ bottomLeft: <Types.Point3>[0, 0, 0],
187
+ bottomRight: <Types.Point3>[0, 0, 0],
188
+ },
189
+ },
190
+ activeHandleIndex: null,
191
+ },
192
+ cachedStats: {},
193
+ },
194
+ };
195
+
196
+ addAnnotation(annotation, element);
197
+
198
+ const viewportIdsToRender = getViewportIdsWithToolToRender(
199
+ element,
200
+ this.getToolName()
201
+ );
202
+
203
+ this.editData = {
204
+ annotation,
205
+ viewportIdsToRender,
206
+ handleIndex: 3,
207
+ movingTextBox: false,
208
+ newAnnotation: true,
209
+ hasMoved: false,
210
+ };
211
+ this._activateDraw(element);
212
+
213
+ hideElementCursor(element);
214
+
215
+ evt.preventDefault();
216
+
217
+ triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender);
218
+
219
+ return annotation;
220
+ };
221
+
222
+ /**
223
+ * It returns if the canvas point is near the provided annotation in the provided
224
+ * element or not. A proximity is passed to the function to determine the
225
+ * proximity of the point to the annotation in number of pixels.
226
+ *
227
+ * @param element - HTML Element
228
+ * @param annotation - Annotation
229
+ * @param canvasCoords - Canvas coordinates
230
+ * @param proximity - Proximity to tool to consider
231
+ * @returns Boolean, whether the canvas point is near tool
232
+ */
233
+ isPointNearTool = (
234
+ element: HTMLDivElement,
235
+ annotation: RectangleROIAnnotation,
236
+ canvasCoords: Types.Point2,
237
+ proximity: number
238
+ ): boolean => {
239
+ const enabledElement = getEnabledElement(element);
240
+ const { viewport } = enabledElement;
241
+
242
+ const { data } = annotation;
243
+ const { points } = data.handles;
244
+
245
+ const canvasPoint1 = viewport.worldToCanvas(points[0]);
246
+ const canvasPoint2 = viewport.worldToCanvas(points[3]);
247
+
248
+ const rect = this._getRectangleImageCoordinates([
249
+ canvasPoint1,
250
+ canvasPoint2,
251
+ ]);
252
+
253
+ const point = [canvasCoords[0], canvasCoords[1]];
254
+ const { left, top, width, height } = rect;
255
+
256
+ const distanceToPoint = rectangle.distanceToPoint(
257
+ [left, top, width, height],
258
+ point as Types.Point2
259
+ );
260
+
261
+ if (distanceToPoint <= proximity) {
262
+ return true;
263
+ }
264
+
265
+ return false;
266
+ };
267
+
268
+ toolSelectedCallback = (
269
+ evt: EventTypes.InteractionEventType,
270
+ annotation: RectangleROIAnnotation
271
+ ): void => {
272
+ const eventDetail = evt.detail;
273
+ const { element } = eventDetail;
274
+
275
+ annotation.highlighted = true;
276
+
277
+ const viewportIdsToRender = getViewportIdsWithToolToRender(
278
+ element,
279
+ this.getToolName()
280
+ );
281
+
282
+ this.editData = {
283
+ annotation,
284
+ viewportIdsToRender,
285
+ movingTextBox: false,
286
+ };
287
+
288
+ this._activateModify(element);
289
+
290
+ hideElementCursor(element);
291
+
292
+ const enabledElement = getEnabledElement(element);
293
+ const { renderingEngine } = enabledElement;
294
+
295
+ triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender);
296
+
297
+ evt.preventDefault();
298
+ };
299
+
300
+ handleSelectedCallback = (
301
+ evt: EventTypes.InteractionEventType,
302
+ annotation: RectangleROIAnnotation,
303
+ handle: ToolHandle
304
+ ): void => {
305
+ const eventDetail = evt.detail;
306
+ const { element } = eventDetail;
307
+ const { data } = annotation;
308
+
309
+ annotation.highlighted = true;
310
+
311
+ let movingTextBox = false;
312
+ let handleIndex;
313
+
314
+ if ((handle as TextBoxHandle).worldPosition) {
315
+ movingTextBox = true;
316
+ } else {
317
+ handleIndex = data.handles.points.findIndex((p) => p === handle);
318
+ }
319
+
320
+ // Find viewports to render on drag.
321
+ const viewportIdsToRender = getViewportIdsWithToolToRender(
322
+ element,
323
+ this.getToolName()
324
+ );
325
+
326
+ this.editData = {
327
+ annotation,
328
+ viewportIdsToRender,
329
+ handleIndex,
330
+ movingTextBox,
331
+ };
332
+ this._activateModify(element);
333
+
334
+ hideElementCursor(element);
335
+
336
+ const enabledElement = getEnabledElement(element);
337
+ const { renderingEngine } = enabledElement;
338
+
339
+ triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender);
340
+
341
+ evt.preventDefault();
342
+ };
343
+
344
+ _endCallback = (evt: EventTypes.InteractionEventType): void => {
345
+ const eventDetail = evt.detail;
346
+ const { element } = eventDetail;
347
+
348
+ const { annotation, viewportIdsToRender, newAnnotation, hasMoved } =
349
+ this.editData;
350
+ const { data } = annotation;
351
+
352
+ if (newAnnotation && !hasMoved) {
353
+ return;
354
+ }
355
+
356
+ data.handles.activeHandleIndex = null;
357
+
358
+ this._deactivateModify(element);
359
+ this._deactivateDraw(element);
360
+
361
+ resetElementCursor(element);
362
+
363
+ const enabledElement = getEnabledElement(element);
364
+ const { renderingEngine } = enabledElement;
365
+
366
+ this.editData = null;
367
+ this.isDrawing = false;
368
+
369
+ if (
370
+ this.isHandleOutsideImage &&
371
+ this.configuration.preventHandleOutsideImage
372
+ ) {
373
+ removeAnnotation(annotation.annotationUID);
374
+ }
375
+
376
+ triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender);
377
+
378
+ if (newAnnotation) {
379
+ const eventType = Events.ANNOTATION_COMPLETED;
380
+
381
+ const eventDetail: AnnotationCompletedEventDetail = {
382
+ annotation,
383
+ };
384
+
385
+ triggerEvent(eventTarget, eventType, eventDetail);
386
+ }
387
+ };
388
+
389
+ _dragCallback = (evt: EventTypes.InteractionEventType): void => {
390
+ this.isDrawing = true;
391
+
392
+ const eventDetail = evt.detail;
393
+ const { element } = eventDetail;
394
+
395
+ const { annotation, viewportIdsToRender, handleIndex, movingTextBox } =
396
+ this.editData;
397
+ const { data } = annotation;
398
+
399
+ if (movingTextBox) {
400
+ // Drag mode - Move the text boxes world position
401
+ const { deltaPoints } = eventDetail as EventTypes.MouseDragEventDetail;
402
+ const worldPosDelta = deltaPoints.world;
403
+
404
+ const { textBox } = data.handles;
405
+ const { worldPosition } = textBox;
406
+
407
+ worldPosition[0] += worldPosDelta[0];
408
+ worldPosition[1] += worldPosDelta[1];
409
+ worldPosition[2] += worldPosDelta[2];
410
+
411
+ textBox.hasMoved = true;
412
+ } else if (handleIndex === undefined) {
413
+ // Drag mode - Moving tool, so move all points by the world points delta
414
+ const { deltaPoints } = eventDetail as EventTypes.MouseDragEventDetail;
415
+ const worldPosDelta = deltaPoints.world;
416
+
417
+ const { points } = data.handles;
418
+
419
+ points.forEach((point) => {
420
+ point[0] += worldPosDelta[0];
421
+ point[1] += worldPosDelta[1];
422
+ point[2] += worldPosDelta[2];
423
+ });
424
+ annotation.invalidated = true;
425
+ } else {
426
+ // Moving handle.
427
+ const { currentPoints } = eventDetail;
428
+ const enabledElement = getEnabledElement(element);
429
+ const { worldToCanvas, canvasToWorld } = enabledElement.viewport;
430
+ const worldPos = currentPoints.world;
431
+
432
+ const { points } = data.handles;
433
+
434
+ // Move this handle.
435
+ points[handleIndex] = [...worldPos];
436
+
437
+ let bottomLeftCanvas;
438
+ let bottomRightCanvas;
439
+ let topLeftCanvas;
440
+ let topRightCanvas;
441
+
442
+ let bottomLeftWorld;
443
+ let bottomRightWorld;
444
+ let topLeftWorld;
445
+ let topRightWorld;
446
+
447
+ switch (handleIndex) {
448
+ case 0:
449
+ case 3:
450
+ // Moving bottomLeft or topRight
451
+
452
+ bottomLeftCanvas = worldToCanvas(points[0]);
453
+ topRightCanvas = worldToCanvas(points[3]);
454
+
455
+ bottomRightCanvas = [topRightCanvas[0], bottomLeftCanvas[1]];
456
+ topLeftCanvas = [bottomLeftCanvas[0], topRightCanvas[1]];
457
+
458
+ bottomRightWorld = canvasToWorld(bottomRightCanvas);
459
+ topLeftWorld = canvasToWorld(topLeftCanvas);
460
+
461
+ points[1] = bottomRightWorld;
462
+ points[2] = topLeftWorld;
463
+
464
+ break;
465
+ case 1:
466
+ case 2:
467
+ // Moving bottomRight or topLeft
468
+ bottomRightCanvas = worldToCanvas(points[1]);
469
+ topLeftCanvas = worldToCanvas(points[2]);
470
+
471
+ bottomLeftCanvas = <Types.Point2>[
472
+ topLeftCanvas[0],
473
+ bottomRightCanvas[1],
474
+ ];
475
+ topRightCanvas = <Types.Point2>[
476
+ bottomRightCanvas[0],
477
+ topLeftCanvas[1],
478
+ ];
479
+
480
+ bottomLeftWorld = canvasToWorld(bottomLeftCanvas);
481
+ topRightWorld = canvasToWorld(topRightCanvas);
482
+
483
+ points[0] = bottomLeftWorld;
484
+ points[3] = topRightWorld;
485
+
486
+ break;
487
+ }
488
+ annotation.invalidated = true;
489
+ }
490
+
491
+ this.editData.hasMoved = true;
492
+
493
+ const enabledElement = getEnabledElement(element);
494
+ const { renderingEngine } = enabledElement;
495
+
496
+ triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender);
497
+ };
498
+
499
+ cancel = (element: HTMLDivElement) => {
500
+ // If it is mid-draw or mid-modify
501
+ if (this.isDrawing) {
502
+ this.isDrawing = false;
503
+ this._deactivateDraw(element);
504
+ this._deactivateModify(element);
505
+ resetElementCursor(element);
506
+
507
+ const { annotation, viewportIdsToRender, newAnnotation } = this.editData;
508
+
509
+ const { data } = annotation;
510
+
511
+ annotation.highlighted = false;
512
+ data.handles.activeHandleIndex = null;
513
+
514
+ const enabledElement = getEnabledElement(element);
515
+ const { renderingEngine } = enabledElement;
516
+
517
+ triggerAnnotationRenderForViewportIds(
518
+ renderingEngine,
519
+ viewportIdsToRender
520
+ );
521
+
522
+ if (newAnnotation) {
523
+ const eventType = Events.ANNOTATION_COMPLETED;
524
+
525
+ const eventDetail: AnnotationCompletedEventDetail = {
526
+ annotation,
527
+ };
528
+
529
+ triggerEvent(eventTarget, eventType, eventDetail);
530
+ }
531
+
532
+ this.editData = null;
533
+ return annotation.annotationUID;
534
+ }
535
+ };
536
+ /**
537
+ * Add event handlers for the modify event loop, and prevent default event prapogation.
538
+ */
539
+ _activateDraw = (element) => {
540
+ state.isInteractingWithTool = true;
541
+
542
+ element.addEventListener(Events.MOUSE_UP, this._endCallback);
543
+ element.addEventListener(Events.MOUSE_DRAG, this._dragCallback);
544
+ element.addEventListener(Events.MOUSE_MOVE, this._dragCallback);
545
+ element.addEventListener(Events.MOUSE_CLICK, this._endCallback);
546
+
547
+ element.addEventListener(Events.TOUCH_END, this._endCallback);
548
+ element.addEventListener(Events.TOUCH_DRAG, this._dragCallback);
549
+ element.addEventListener(Events.TOUCH_TAP, this._endCallback);
550
+ };
551
+
552
+ /**
553
+ * Add event handlers for the modify event loop, and prevent default event prapogation.
554
+ */
555
+ _deactivateDraw = (element) => {
556
+ state.isInteractingWithTool = false;
557
+
558
+ element.removeEventListener(Events.MOUSE_UP, this._endCallback);
559
+ element.removeEventListener(Events.MOUSE_DRAG, this._dragCallback);
560
+ element.removeEventListener(Events.MOUSE_MOVE, this._dragCallback);
561
+ element.removeEventListener(Events.MOUSE_CLICK, this._endCallback);
562
+
563
+ element.removeEventListener(Events.TOUCH_END, this._endCallback);
564
+ element.removeEventListener(Events.TOUCH_DRAG, this._dragCallback);
565
+ element.removeEventListener(Events.TOUCH_TAP, this._endCallback);
566
+ };
567
+
568
+ /**
569
+ * Add event handlers for the modify event loop, and prevent default event prapogation.
570
+ */
571
+ _activateModify = (element) => {
572
+ state.isInteractingWithTool = true;
573
+
574
+ element.addEventListener(Events.MOUSE_UP, this._endCallback);
575
+ element.addEventListener(Events.MOUSE_DRAG, this._dragCallback);
576
+ element.addEventListener(Events.MOUSE_CLICK, this._endCallback);
577
+
578
+ element.addEventListener(Events.TOUCH_END, this._endCallback);
579
+ element.addEventListener(Events.TOUCH_DRAG, this._dragCallback);
580
+ element.addEventListener(Events.TOUCH_TAP, this._endCallback);
581
+ };
582
+
583
+ /**
584
+ * Remove event handlers for the modify event loop, and enable default event propagation.
585
+ */
586
+ _deactivateModify = (element) => {
587
+ state.isInteractingWithTool = false;
588
+
589
+ element.removeEventListener(Events.MOUSE_UP, this._endCallback);
590
+ element.removeEventListener(Events.MOUSE_DRAG, this._dragCallback);
591
+ element.removeEventListener(Events.MOUSE_CLICK, this._endCallback);
592
+
593
+ element.removeEventListener(Events.TOUCH_END, this._endCallback);
594
+ element.removeEventListener(Events.TOUCH_DRAG, this._dragCallback);
595
+ element.removeEventListener(Events.TOUCH_TAP, this._endCallback);
596
+ };
597
+
598
+ /**
599
+ * it is used to draw the rectangleROI annotation in each
600
+ * request animation frame. It calculates the updated cached statistics if
601
+ * data is invalidated and cache it.
602
+ *
603
+ * @param enabledElement - The Cornerstone's enabledElement.
604
+ * @param svgDrawingHelper - The svgDrawingHelper providing the context for drawing.
605
+ */
606
+ renderAnnotation = (
607
+ enabledElement: Types.IEnabledElement,
608
+ svgDrawingHelper: SVGDrawingHelper
609
+ ): boolean => {
610
+ let renderStatus = false;
611
+ const { viewport } = enabledElement;
612
+ const { element } = viewport;
613
+
614
+ let annotations = getAnnotations(this.getToolName(), element);
615
+
616
+ if (!annotations?.length) {
617
+ return renderStatus;
618
+ }
619
+
620
+ annotations = this.filterInteractableAnnotationsForElement(
621
+ element,
622
+ annotations
623
+ );
624
+
625
+ if (!annotations?.length) {
626
+ return renderStatus;
627
+ }
628
+
629
+ const targetId = this.getTargetId(viewport);
630
+ const renderingEngine = viewport.getRenderingEngine();
631
+
632
+ const styleSpecifier: StyleSpecifier = {
633
+ toolGroupId: this.toolGroupId,
634
+ toolName: this.getToolName(),
635
+ viewportId: enabledElement.viewport.id,
636
+ };
637
+
638
+ for (let i = 0; i < annotations.length; i++) {
639
+ const annotation = annotations[i] as RectangleROIAnnotation;
640
+ const { annotationUID, data } = annotation;
641
+ const { points, activeHandleIndex } = data.handles;
642
+ const canvasCoordinates = points.map((p) => viewport.worldToCanvas(p));
643
+
644
+ styleSpecifier.annotationUID = annotationUID;
645
+
646
+ const lineWidth = this.getStyle('lineWidth', styleSpecifier, annotation);
647
+ const lineDash = this.getStyle('lineDash', styleSpecifier, annotation);
648
+ const color = this.getStyle('color', styleSpecifier, annotation);
649
+
650
+ const { viewPlaneNormal, viewUp } = viewport.getCamera();
651
+
652
+ // If cachedStats does not exist, or the unit is missing (as part of import/hydration etc.),
653
+ // force to recalculate the stats from the points
654
+ if (
655
+ !data.cachedStats[targetId] ||
656
+ data.cachedStats[targetId].unit === undefined
657
+ ) {
658
+ data.cachedStats[targetId] = {
659
+ Modality: null,
660
+ area: null,
661
+ max: null,
662
+ mean: null,
663
+ stdDev: null,
664
+ areaUnit: null,
665
+ };
666
+
667
+ this._calculateCachedStats(
668
+ annotation,
669
+ viewPlaneNormal,
670
+ viewUp,
671
+ renderingEngine,
672
+ enabledElement
673
+ );
674
+ } else if (annotation.invalidated) {
675
+ this._throttledCalculateCachedStats(
676
+ annotation,
677
+ viewPlaneNormal,
678
+ viewUp,
679
+ renderingEngine,
680
+ enabledElement
681
+ );
682
+
683
+ // If the invalidated data is as a result of volumeViewport manipulation
684
+ // of the tools, we need to invalidate the related stackViewports data if
685
+ // they are not at the referencedImageId, so that
686
+ // when scrolling to the related slice in which the tool were manipulated
687
+ // we re-render the correct tool position. This is due to stackViewport
688
+ // which doesn't have the full volume at each time, and we are only working
689
+ // on one slice at a time.
690
+ if (viewport instanceof VolumeViewport) {
691
+ const { referencedImageId } = annotation.metadata;
692
+
693
+ // invalidate all the relevant stackViewports if they are not
694
+ // at the referencedImageId
695
+ for (const targetId in data.cachedStats) {
696
+ if (targetId.startsWith('imageId')) {
697
+ const viewports = renderingEngine.getStackViewports();
698
+
699
+ const invalidatedStack = viewports.find((vp) => {
700
+ // The stack viewport that contains the imageId but is not
701
+ // showing it currently
702
+ const referencedImageURI =
703
+ csUtils.imageIdToURI(referencedImageId);
704
+ const hasImageURI = vp.hasImageURI(referencedImageURI);
705
+ const currentImageURI = csUtils.imageIdToURI(
706
+ vp.getCurrentImageId()
707
+ );
708
+ return hasImageURI && currentImageURI !== referencedImageURI;
709
+ });
710
+
711
+ if (invalidatedStack) {
712
+ delete data.cachedStats[targetId];
713
+ }
714
+ }
715
+ }
716
+ }
717
+ }
718
+
719
+ // If rendering engine has been destroyed while rendering
720
+ if (!viewport.getRenderingEngine()) {
721
+ console.warn('Rendering Engine has been destroyed');
722
+ return renderStatus;
723
+ }
724
+
725
+ let activeHandleCanvasCoords;
726
+
727
+ if (!isAnnotationVisible(annotationUID)) {
728
+ continue;
729
+ }
730
+
731
+ if (
732
+ !isAnnotationLocked(annotation) &&
733
+ !this.editData &&
734
+ activeHandleIndex !== null
735
+ ) {
736
+ // Not locked or creating and hovering over handle, so render handle.
737
+ activeHandleCanvasCoords = [canvasCoordinates[activeHandleIndex]];
738
+ }
739
+
740
+ if (activeHandleCanvasCoords) {
741
+ const handleGroupUID = '0';
742
+
743
+ drawHandlesSvg(
744
+ svgDrawingHelper,
745
+ annotationUID,
746
+ handleGroupUID,
747
+ activeHandleCanvasCoords,
748
+ {
749
+ color,
750
+ }
751
+ );
752
+ }
753
+
754
+ const dataId = `${annotationUID}-rect`;
755
+ const rectangleUID = '0';
756
+ drawRectSvg(
757
+ svgDrawingHelper,
758
+ annotationUID,
759
+ rectangleUID,
760
+ canvasCoordinates[0],
761
+ canvasCoordinates[3],
762
+ {
763
+ color,
764
+ lineDash,
765
+ lineWidth,
766
+ },
767
+ dataId
768
+ );
769
+
770
+ renderStatus = true;
771
+
772
+ const isPreScaled = isViewportPreScaled(viewport, targetId);
773
+
774
+ const textLines = this._getTextLines(data, targetId, isPreScaled);
775
+ if (!textLines || textLines.length === 0) {
776
+ continue;
777
+ }
778
+
779
+ if (!data.handles.textBox.hasMoved) {
780
+ const canvasTextBoxCoords = getTextBoxCoordsCanvas(canvasCoordinates);
781
+
782
+ data.handles.textBox.worldPosition =
783
+ viewport.canvasToWorld(canvasTextBoxCoords);
784
+ }
785
+
786
+ const textBoxPosition = viewport.worldToCanvas(
787
+ data.handles.textBox.worldPosition
788
+ );
789
+
790
+ const textBoxUID = '1';
791
+ const boundingBox = drawLinkedTextBoxSvg(
792
+ svgDrawingHelper,
793
+ annotationUID,
794
+ textBoxUID,
795
+ textLines,
796
+ textBoxPosition,
797
+ canvasCoordinates,
798
+ {},
799
+ this.getLinkedTextBoxStyle(styleSpecifier, annotation)
800
+ );
801
+
802
+ const { x: left, y: top, width, height } = boundingBox;
803
+
804
+ data.handles.textBox.worldBoundingBox = {
805
+ topLeft: viewport.canvasToWorld([left, top]),
806
+ topRight: viewport.canvasToWorld([left + width, top]),
807
+ bottomLeft: viewport.canvasToWorld([left, top + height]),
808
+ bottomRight: viewport.canvasToWorld([left + width, top + height]),
809
+ };
810
+ }
811
+
812
+ return renderStatus;
813
+ };
814
+
815
+ _getRectangleImageCoordinates = (
816
+ points: Array<Types.Point2>
817
+ ): {
818
+ left: number;
819
+ top: number;
820
+ width: number;
821
+ height: number;
822
+ } => {
823
+ const [point0, point1] = points;
824
+
825
+ return {
826
+ left: Math.min(point0[0], point1[0]),
827
+ top: Math.min(point0[1], point1[1]),
828
+ width: Math.abs(point0[0] - point1[0]),
829
+ height: Math.abs(point0[1] - point1[1]),
830
+ };
831
+ };
832
+
833
+ /**
834
+ * _getTextLines - Returns the Area, mean and std deviation of the area of the
835
+ * target volume enclosed by the rectangle.
836
+ *
837
+ * @param data - The annotation tool-specific data.
838
+ * @param targetId - The volumeId of the volume to display the stats for.
839
+ * @param isPreScaled - Whether the viewport is pre-scaled or not.
840
+ */
841
+ _getTextLines = (
842
+ data,
843
+ targetId: string,
844
+ isPreScaled: boolean
845
+ ): string[] | undefined => {
846
+ const cachedVolumeStats = data.cachedStats[targetId];
847
+ const { area, mean, max, stdDev, Modality, areaUnit } = cachedVolumeStats;
848
+
849
+ if (mean === undefined) {
850
+ return;
851
+ }
852
+
853
+ const textLines: string[] = [];
854
+ const unit = getModalityUnit(Modality, isPreScaled);
855
+
856
+ textLines.push(`Area: ${area.toFixed(2)} ${areaUnit}\xb2`);
857
+ textLines.push(`Mean: ${mean.toFixed(2)} ${unit}`);
858
+ textLines.push(`Max: ${max.toFixed(2)} ${unit}`);
859
+ textLines.push(`Std Dev: ${stdDev.toFixed(2)} ${unit}`);
860
+
861
+ return textLines;
862
+ };
863
+
864
+ /**
865
+ * _calculateCachedStats - For each volume in the frame of reference that a
866
+ * tool instance in particular viewport defines as its target volume, find the
867
+ * volume coordinates (i,j,k) being probed by the two corners. One of i,j or k
868
+ * will be constant across the two points. In the other two directions iterate
869
+ * over the voxels and calculate the first and second-order statistics.
870
+ *
871
+ * @param data - The annotation tool-specific data.
872
+ * @param viewPlaneNormal - The normal vector of the camera.
873
+ * @param viewUp - The viewUp vector of the camera.
874
+ */
875
+ _calculateCachedStats = (
876
+ annotation,
877
+ viewPlaneNormal,
878
+ viewUp,
879
+ renderingEngine,
880
+ enabledElement
881
+ ) => {
882
+ const { data } = annotation;
883
+ const { viewportId, renderingEngineId } = enabledElement;
884
+
885
+ const worldPos1 = data.handles.points[0];
886
+ const worldPos2 = data.handles.points[3];
887
+ const { cachedStats } = data;
888
+
889
+ const targetIds = Object.keys(cachedStats);
890
+
891
+ for (let i = 0; i < targetIds.length; i++) {
892
+ const targetId = targetIds[i];
893
+
894
+ const image = this.getTargetIdImage(targetId, renderingEngine);
895
+
896
+ // If image does not exists for the targetId, skip. This can be due
897
+ // to various reasons such as if the target was a volumeViewport, and
898
+ // the volumeViewport has been decached in the meantime.
899
+ if (!image) {
900
+ continue;
901
+ }
902
+
903
+ const { dimensions, imageData, metadata, hasPixelSpacing } = image;
904
+ const scalarData =
905
+ 'getScalarData' in image ? image.getScalarData() : image.scalarData;
906
+
907
+ const worldPos1Index = transformWorldToIndex(imageData, worldPos1);
908
+
909
+ worldPos1Index[0] = Math.floor(worldPos1Index[0]);
910
+ worldPos1Index[1] = Math.floor(worldPos1Index[1]);
911
+ worldPos1Index[2] = Math.floor(worldPos1Index[2]);
912
+
913
+ const worldPos2Index = transformWorldToIndex(imageData, worldPos2);
914
+
915
+ worldPos2Index[0] = Math.floor(worldPos2Index[0]);
916
+ worldPos2Index[1] = Math.floor(worldPos2Index[1]);
917
+ worldPos2Index[2] = Math.floor(worldPos2Index[2]);
918
+
919
+ // Check if one of the indexes are inside the volume, this then gives us
920
+ // Some area to do stats over.
921
+
922
+ if (this._isInsideVolume(worldPos1Index, worldPos2Index, dimensions)) {
923
+ this.isHandleOutsideImage = false;
924
+
925
+ // Calculate index bounds to iterate over
926
+
927
+ const iMin = Math.min(worldPos1Index[0], worldPos2Index[0]);
928
+ const iMax = Math.max(worldPos1Index[0], worldPos2Index[0]);
929
+
930
+ const jMin = Math.min(worldPos1Index[1], worldPos2Index[1]);
931
+ const jMax = Math.max(worldPos1Index[1], worldPos2Index[1]);
932
+
933
+ const kMin = Math.min(worldPos1Index[2], worldPos2Index[2]);
934
+ const kMax = Math.max(worldPos1Index[2], worldPos2Index[2]);
935
+
936
+ const { worldWidth, worldHeight } = getWorldWidthAndHeightFromCorners(
937
+ viewPlaneNormal,
938
+ viewUp,
939
+ worldPos1,
940
+ worldPos2
941
+ );
942
+
943
+ const area = Math.abs(worldWidth * worldHeight);
944
+
945
+ let count = 0;
946
+ let mean = 0;
947
+ let stdDev = 0;
948
+ let max = -Infinity;
949
+
950
+ const yMultiple = dimensions[0];
951
+ const zMultiple = dimensions[0] * dimensions[1];
952
+
953
+ //Todo: this can be replaced by pointInShapeCallback....
954
+ // This is a triple loop, but one of these 3 values will be constant
955
+ // In the planar view.
956
+ for (let k = kMin; k <= kMax; k++) {
957
+ for (let j = jMin; j <= jMax; j++) {
958
+ for (let i = iMin; i <= iMax; i++) {
959
+ const value = scalarData[k * zMultiple + j * yMultiple + i];
960
+
961
+ if (value > max) {
962
+ max = value;
963
+ }
964
+
965
+ count++;
966
+ mean += value;
967
+ }
968
+ }
969
+ }
970
+
971
+ mean /= count;
972
+
973
+ for (let k = kMin; k <= kMax; k++) {
974
+ for (let j = jMin; j <= jMax; j++) {
975
+ for (let i = iMin; i <= iMax; i++) {
976
+ const value = scalarData[k * zMultiple + j * yMultiple + i];
977
+
978
+ const valueMinusMean = value - mean;
979
+
980
+ stdDev += valueMinusMean * valueMinusMean;
981
+ }
982
+ }
983
+ }
984
+
985
+ stdDev /= count;
986
+ stdDev = Math.sqrt(stdDev);
987
+
988
+ cachedStats[targetId] = {
989
+ Modality: metadata.Modality,
990
+ area,
991
+ mean,
992
+ stdDev,
993
+ max,
994
+ areaUnit: hasPixelSpacing ? 'mm' : 'px',
995
+ };
996
+ } else {
997
+ this.isHandleOutsideImage = true;
998
+ cachedStats[targetId] = {
999
+ Modality: metadata.Modality,
1000
+ };
1001
+ }
1002
+ }
1003
+
1004
+ annotation.invalidated = false;
1005
+
1006
+ // Dispatching annotation modified
1007
+ const eventType = Events.ANNOTATION_MODIFIED;
1008
+
1009
+ const eventDetail: AnnotationModifiedEventDetail = {
1010
+ annotation,
1011
+ viewportId,
1012
+ renderingEngineId,
1013
+ };
1014
+ triggerEvent(eventTarget, eventType, eventDetail);
1015
+
1016
+ return cachedStats;
1017
+ };
1018
+
1019
+ _isInsideVolume = (index1, index2, dimensions) => {
1020
+ return (
1021
+ csUtils.indexWithinDimensions(index1, dimensions) &&
1022
+ csUtils.indexWithinDimensions(index2, dimensions)
1023
+ );
1024
+ };
1025
+ }
1026
+
1027
+ RectangleROITool.toolName = 'RectangleROI';
1028
+ export default RectangleROITool;