@cornerstonejs/tools 0.56.2 → 0.56.3

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,1070 @@
1
+ import { AnnotationTool } from '../base';
2
+
3
+ import {
4
+ getEnabledElement,
5
+ VolumeViewport,
6
+ eventTarget,
7
+ triggerEvent,
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/annotation/annotationState';
18
+ import { isAnnotationLocked } from '../../stateManagement/annotation/annotationLocking';
19
+ import { isAnnotationVisible } from '../../stateManagement/annotation/annotationVisibility';
20
+ import {
21
+ drawCircle as drawCircleSvg,
22
+ drawHandles as drawHandlesSvg,
23
+ drawLinkedTextBox as drawLinkedTextBoxSvg,
24
+ } from '../../drawingSvg';
25
+ import { state } from '../../store';
26
+ import { Events } from '../../enums';
27
+ import { getViewportIdsWithToolToRender } from '../../utilities/viewportFilters';
28
+ import { getTextBoxCoordsCanvas } from '../../utilities/drawing';
29
+ import getWorldWidthAndHeightFromTwoPoints from '../../utilities/planar/getWorldWidthAndHeightFromTwoPoints';
30
+ import {
31
+ resetElementCursor,
32
+ hideElementCursor,
33
+ } from '../../cursors/elementCursor';
34
+ import {
35
+ EventTypes,
36
+ ToolHandle,
37
+ TextBoxHandle,
38
+ PublicToolProps,
39
+ ToolProps,
40
+ InteractionTypes,
41
+ SVGDrawingHelper,
42
+ } from '../../types';
43
+ import { CircleROIAnnotation } from '../../types/ToolSpecificAnnotationTypes';
44
+
45
+ import {
46
+ AnnotationCompletedEventDetail,
47
+ AnnotationModifiedEventDetail,
48
+ MouseDragEventType,
49
+ MouseMoveEventType,
50
+ } from '../../types/EventTypes';
51
+ import triggerAnnotationRenderForViewportIds from '../../utilities/triggerAnnotationRenderForViewportIds';
52
+ import { pointInShapeCallback } from '../../utilities';
53
+ import { StyleSpecifier } from '../../types/AnnotationStyle';
54
+ import { getModalityUnit } from '../../utilities/getModalityUnit';
55
+ import { isViewportPreScaled } from '../../utilities/viewport/isViewportPreScaled';
56
+ import {
57
+ getCanvasCircleCorners,
58
+ getCanvasCircleRadius,
59
+ } from '../../utilities/math/circle';
60
+ import { pointInEllipse } from '../../utilities/math/ellipse';
61
+
62
+ const { transformWorldToIndex } = csUtils;
63
+
64
+ /**
65
+ * CircleROITool let you draw annotations that measures the statistics
66
+ * such as area, max, mean and stdDev of an elliptical region of interest.
67
+ * You can use CircleROITool in all perpendicular views (axial, sagittal, coronal).
68
+ * Note: annotation tools in cornerstone3DTools exists in the exact location
69
+ * in the physical 3d space, as a result, by default, all annotations that are
70
+ * drawing in the same frameOfReference will get shared between viewports that
71
+ * are in the same frameOfReference. Circle tool's text box lines are dynamically
72
+ * generated based on the viewport's underlying Modality. For instance, if
73
+ * the viewport is displaying CT, the text box will shown the statistics in Hounsfield units,
74
+ * and if the viewport is displaying PET, the text box will show the statistics in
75
+ * SUV units.
76
+ *
77
+ * The resulting annotation's data (statistics) and metadata (the
78
+ * state of the viewport while drawing was happening) will get added to the
79
+ * ToolState manager and can be accessed from the ToolState by calling getAnnotations
80
+ * or similar methods.
81
+ *
82
+ * Changing tool configuration (see below) you can make the tool to draw the center
83
+ * point circle with a given radius.
84
+ *
85
+ * ```js
86
+ * cornerstoneTools.addTool(CircleROITool)
87
+ *
88
+ * const toolGroup = ToolGroupManager.createToolGroup('toolGroupId')
89
+ *
90
+ * toolGroup.addTool(CircleROITool.toolName)
91
+ *
92
+ * toolGroup.addViewport('viewportId', 'renderingEngineId')
93
+ *
94
+ * toolGroup.setToolActive(CircleROITool.toolName, {
95
+ * bindings: [
96
+ * {
97
+ * mouseButton: MouseBindings.Primary, // Left Click
98
+ * },
99
+ * ],
100
+ * })
101
+ *
102
+ * // draw a circle at the center point with 4px radius.
103
+ * toolGroup.setToolConfiguration(CircleROITool.toolName, {
104
+ * centerPointRadius: 4,
105
+ * });
106
+ * ```
107
+ *
108
+ * Read more in the Docs section of the website.
109
+ */
110
+ class CircleROITool extends AnnotationTool {
111
+ static toolName;
112
+ touchDragCallback: any;
113
+ mouseDragCallback: any;
114
+ _throttledCalculateCachedStats: any;
115
+ editData: {
116
+ annotation: any;
117
+ viewportIdsToRender: Array<string>;
118
+ handleIndex?: number;
119
+ movingTextBox?: boolean;
120
+ newAnnotation?: boolean;
121
+ hasMoved?: boolean;
122
+ } | null;
123
+ isDrawing: boolean;
124
+ isHandleOutsideImage = false;
125
+
126
+ constructor(
127
+ toolProps: PublicToolProps = {},
128
+ defaultToolProps: ToolProps = {
129
+ supportedInteractionTypes: ['Mouse', 'Touch'],
130
+ configuration: {
131
+ shadow: true,
132
+ preventHandleOutsideImage: false,
133
+ // Radius of the circle to draw at the center point of the circle.
134
+ // Set this zero(0) in order not to draw the circle.
135
+ centerPointRadius: 0,
136
+ },
137
+ }
138
+ ) {
139
+ super(toolProps, defaultToolProps);
140
+
141
+ this._throttledCalculateCachedStats = throttle(
142
+ this._calculateCachedStats,
143
+ 100,
144
+ { trailing: true }
145
+ );
146
+ }
147
+
148
+ /**
149
+ * Based on the current position of the mouse and the current imageId to create
150
+ * a CircleROI Annotation and stores it in the annotationManager
151
+ *
152
+ * @param evt - EventTypes.NormalizedMouseEventType
153
+ * @returns The annotation object.
154
+ *
155
+ */
156
+ addNewAnnotation = (
157
+ evt: EventTypes.InteractionEventType
158
+ ): CircleROIAnnotation => {
159
+ const eventDetail = evt.detail;
160
+ const { currentPoints, element } = eventDetail;
161
+ const worldPos = currentPoints.world;
162
+ const canvasPos = currentPoints.canvas;
163
+
164
+ const enabledElement = getEnabledElement(element);
165
+ const { viewport, renderingEngine } = enabledElement;
166
+
167
+ this.isDrawing = true;
168
+
169
+ const camera = viewport.getCamera();
170
+ const { viewPlaneNormal, viewUp } = camera;
171
+
172
+ const referencedImageId = this.getReferencedImageId(
173
+ viewport,
174
+ worldPos,
175
+ viewPlaneNormal,
176
+ viewUp
177
+ );
178
+
179
+ const FrameOfReferenceUID = viewport.getFrameOfReferenceUID();
180
+
181
+ const annotation = {
182
+ highlighted: true,
183
+ invalidated: true,
184
+ metadata: {
185
+ toolName: this.getToolName(),
186
+ viewPlaneNormal: <Types.Point3>[...viewPlaneNormal],
187
+ viewUp: <Types.Point3>[...viewUp],
188
+ FrameOfReferenceUID,
189
+ referencedImageId,
190
+ },
191
+ data: {
192
+ label: '',
193
+ handles: {
194
+ textBox: {
195
+ hasMoved: false,
196
+ worldPosition: <Types.Point3>[0, 0, 0],
197
+ worldBoundingBox: {
198
+ topLeft: <Types.Point3>[0, 0, 0],
199
+ topRight: <Types.Point3>[0, 0, 0],
200
+ bottomLeft: <Types.Point3>[0, 0, 0],
201
+ bottomRight: <Types.Point3>[0, 0, 0],
202
+ },
203
+ },
204
+ points: [[...worldPos], [...worldPos]] as [
205
+ Types.Point3, // center
206
+ Types.Point3 // end
207
+ ],
208
+ activeHandleIndex: null,
209
+ },
210
+ cachedStats: {},
211
+ },
212
+ };
213
+
214
+ addAnnotation(annotation, element);
215
+
216
+ const viewportIdsToRender = getViewportIdsWithToolToRender(
217
+ element,
218
+ this.getToolName()
219
+ );
220
+
221
+ this.editData = {
222
+ annotation,
223
+ viewportIdsToRender,
224
+ newAnnotation: true,
225
+ hasMoved: false,
226
+ };
227
+ this._activateDraw(element);
228
+
229
+ hideElementCursor(element);
230
+
231
+ evt.preventDefault();
232
+
233
+ triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender);
234
+
235
+ return annotation;
236
+ };
237
+
238
+ /**
239
+ * It returns if the canvas point is near the provided annotation in the provided
240
+ * element or not. A proximity is passed to the function to determine the
241
+ * proximity of the point to the annotation in number of pixels.
242
+ *
243
+ * @param element - HTML Element
244
+ * @param annotation - Annotation
245
+ * @param canvasCoords - Canvas coordinates
246
+ * @param proximity - Proximity to tool to consider
247
+ * @returns Boolean, whether the canvas point is near tool
248
+ */
249
+ isPointNearTool = (
250
+ element: HTMLDivElement,
251
+ annotation: CircleROIAnnotation,
252
+ canvasCoords: Types.Point2,
253
+ proximity: number
254
+ ): boolean => {
255
+ const enabledElement = getEnabledElement(element);
256
+ const { viewport } = enabledElement;
257
+
258
+ const { data } = annotation;
259
+ const { points } = data.handles;
260
+
261
+ // For some reason Typescript doesn't understand this, so we need to be
262
+ // more specific about the type
263
+ const canvasCoordinates = points.map((p) => viewport.worldToCanvas(p)) as [
264
+ Types.Point2,
265
+ Types.Point2
266
+ ];
267
+
268
+ const radius = getCanvasCircleRadius(canvasCoordinates);
269
+ const radiusPoint = getCanvasCircleRadius([
270
+ canvasCoordinates[0],
271
+ canvasCoords,
272
+ ]);
273
+
274
+ if (Math.abs(radiusPoint - radius) < proximity / 2) return true;
275
+
276
+ return false;
277
+ };
278
+
279
+ toolSelectedCallback = (
280
+ evt: EventTypes.InteractionEventType,
281
+ annotation: CircleROIAnnotation
282
+ ): void => {
283
+ const eventDetail = evt.detail;
284
+ const { element } = eventDetail;
285
+
286
+ annotation.highlighted = true;
287
+
288
+ const viewportIdsToRender = getViewportIdsWithToolToRender(
289
+ element,
290
+ this.getToolName()
291
+ );
292
+
293
+ this.editData = {
294
+ annotation,
295
+ viewportIdsToRender,
296
+ movingTextBox: false,
297
+ };
298
+
299
+ hideElementCursor(element);
300
+
301
+ this._activateModify(element);
302
+
303
+ const enabledElement = getEnabledElement(element);
304
+ const { renderingEngine } = enabledElement;
305
+
306
+ triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender);
307
+
308
+ evt.preventDefault();
309
+ };
310
+
311
+ handleSelectedCallback = (
312
+ evt: EventTypes.InteractionEventType,
313
+ annotation: CircleROIAnnotation,
314
+ handle: ToolHandle
315
+ ): void => {
316
+ const eventDetail = evt.detail;
317
+ const { element } = eventDetail;
318
+ const { data } = annotation;
319
+
320
+ annotation.highlighted = true;
321
+
322
+ let movingTextBox = false;
323
+ let handleIndex;
324
+
325
+ if ((handle as TextBoxHandle).worldPosition) {
326
+ movingTextBox = true;
327
+ } else {
328
+ const { points } = data.handles;
329
+
330
+ handleIndex = points.findIndex((p) => p === handle);
331
+ }
332
+
333
+ // Find viewports to render on drag.
334
+ const viewportIdsToRender = getViewportIdsWithToolToRender(
335
+ element,
336
+ this.getToolName()
337
+ );
338
+
339
+ this.editData = {
340
+ annotation,
341
+ viewportIdsToRender,
342
+ handleIndex,
343
+ movingTextBox,
344
+ };
345
+ this._activateModify(element);
346
+
347
+ hideElementCursor(element);
348
+
349
+ const enabledElement = getEnabledElement(element);
350
+ const { renderingEngine } = enabledElement;
351
+
352
+ triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender);
353
+
354
+ evt.preventDefault();
355
+ };
356
+
357
+ _endCallback = (evt: EventTypes.InteractionEventType): void => {
358
+ const eventDetail = evt.detail;
359
+ const { element } = eventDetail;
360
+
361
+ const { annotation, viewportIdsToRender, newAnnotation, hasMoved } =
362
+ this.editData;
363
+ const { data } = annotation;
364
+
365
+ if (newAnnotation && !hasMoved) {
366
+ return;
367
+ }
368
+
369
+ // Circle ROI tool should reset its highlight to false on mouse up (as opposed
370
+ // to other tools that keep it highlighted until the user moves. The reason
371
+ // is that we use top-left and bottom-right handles to define the circle,
372
+ // and they are by definition not in the circle on mouse up.
373
+ annotation.highlighted = false;
374
+ data.handles.activeHandleIndex = null;
375
+
376
+ this._deactivateModify(element);
377
+ this._deactivateDraw(element);
378
+
379
+ resetElementCursor(element);
380
+
381
+ const enabledElement = getEnabledElement(element);
382
+ const { renderingEngine } = enabledElement;
383
+
384
+ this.editData = null;
385
+ this.isDrawing = false;
386
+
387
+ if (
388
+ this.isHandleOutsideImage &&
389
+ this.configuration.preventHandleOutsideImage
390
+ ) {
391
+ removeAnnotation(annotation.annotationUID);
392
+ }
393
+
394
+ triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender);
395
+
396
+ if (newAnnotation) {
397
+ const eventType = Events.ANNOTATION_COMPLETED;
398
+
399
+ const eventDetail: AnnotationCompletedEventDetail = {
400
+ annotation,
401
+ };
402
+
403
+ triggerEvent(eventTarget, eventType, eventDetail);
404
+ }
405
+ };
406
+
407
+ _dragDrawCallback = (evt: EventTypes.InteractionEventType): void => {
408
+ this.isDrawing = true;
409
+ const eventDetail = evt.detail;
410
+ const { element } = eventDetail;
411
+ const { currentPoints } = eventDetail;
412
+ const currentCanvasPoints = currentPoints.canvas;
413
+ const enabledElement = getEnabledElement(element);
414
+ const { renderingEngine, viewport } = enabledElement;
415
+ const { canvasToWorld } = viewport;
416
+
417
+ //////
418
+ const { annotation, viewportIdsToRender } = this.editData;
419
+ const { data } = annotation;
420
+
421
+ data.handles.points = [
422
+ data.handles.points[0], // center stays
423
+ canvasToWorld(currentCanvasPoints), // end point moves (changing radius)
424
+ ];
425
+
426
+ annotation.invalidated = true;
427
+
428
+ this.editData.hasMoved = true;
429
+
430
+ triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender);
431
+ };
432
+
433
+ _dragModifyCallback = (evt: EventTypes.InteractionEventType): void => {
434
+ this.isDrawing = true;
435
+ const eventDetail = evt.detail;
436
+ const { element } = eventDetail;
437
+
438
+ const { annotation, viewportIdsToRender, handleIndex, movingTextBox } =
439
+ this.editData;
440
+ const { data } = annotation;
441
+
442
+ if (movingTextBox) {
443
+ const { deltaPoints } = eventDetail;
444
+ const worldPosDelta = deltaPoints.world;
445
+
446
+ const { textBox } = data.handles;
447
+ const { worldPosition } = textBox;
448
+
449
+ worldPosition[0] += worldPosDelta[0];
450
+ worldPosition[1] += worldPosDelta[1];
451
+ worldPosition[2] += worldPosDelta[2];
452
+
453
+ textBox.hasMoved = true;
454
+ } else if (handleIndex === undefined) {
455
+ // Moving tool
456
+ const { deltaPoints } = eventDetail;
457
+ const worldPosDelta = deltaPoints.world;
458
+
459
+ const points = data.handles.points;
460
+
461
+ points.forEach((point) => {
462
+ point[0] += worldPosDelta[0];
463
+ point[1] += worldPosDelta[1];
464
+ point[2] += worldPosDelta[2];
465
+ });
466
+ annotation.invalidated = true;
467
+ } else {
468
+ this._dragHandle(evt);
469
+ annotation.invalidated = true;
470
+ }
471
+
472
+ const enabledElement = getEnabledElement(element);
473
+ const { renderingEngine } = enabledElement;
474
+
475
+ triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender);
476
+ };
477
+
478
+ _dragHandle = (evt: EventTypes.InteractionEventType): void => {
479
+ const eventDetail = evt.detail;
480
+ const { element } = eventDetail;
481
+ const enabledElement = getEnabledElement(element);
482
+ const { canvasToWorld, worldToCanvas } = enabledElement.viewport;
483
+
484
+ const { annotation, handleIndex } = this.editData;
485
+ const { data } = annotation;
486
+ const { points } = data.handles;
487
+
488
+ const canvasCoordinates = points.map((p) => worldToCanvas(p));
489
+
490
+ // Move current point in that direction.
491
+ // Move other points in opposite direction.
492
+
493
+ const { currentPoints } = eventDetail;
494
+ const currentCanvasPoints = currentPoints.canvas;
495
+
496
+ if (handleIndex === 0) {
497
+ // Dragging center, move the circle ROI
498
+ const dXCanvas = currentCanvasPoints[0] - canvasCoordinates[0][0];
499
+ const dYCanvas = currentCanvasPoints[1] - canvasCoordinates[0][1];
500
+
501
+ const canvasCenter = currentCanvasPoints as Types.Point2;
502
+ const canvasEnd = <Types.Point2>[
503
+ canvasCoordinates[1][0] + dXCanvas,
504
+ canvasCoordinates[1][1] + dYCanvas,
505
+ ];
506
+
507
+ points[0] = canvasToWorld(canvasCenter);
508
+ points[1] = canvasToWorld(canvasEnd);
509
+ } else {
510
+ // Dragging end point, center stays
511
+ points[1] = canvasToWorld(currentCanvasPoints);
512
+ }
513
+ };
514
+
515
+ cancel = (element: HTMLDivElement) => {
516
+ // If it is mid-draw or mid-modify
517
+ if (this.isDrawing) {
518
+ this.isDrawing = false;
519
+ this._deactivateDraw(element);
520
+ this._deactivateModify(element);
521
+ resetElementCursor(element);
522
+
523
+ const { annotation, viewportIdsToRender, newAnnotation } = this.editData;
524
+ const { data } = annotation;
525
+
526
+ annotation.highlighted = false;
527
+ data.handles.activeHandleIndex = null;
528
+
529
+ const enabledElement = getEnabledElement(element);
530
+ const { renderingEngine } = enabledElement;
531
+
532
+ triggerAnnotationRenderForViewportIds(
533
+ renderingEngine,
534
+ viewportIdsToRender
535
+ );
536
+
537
+ if (newAnnotation) {
538
+ const eventType = Events.ANNOTATION_COMPLETED;
539
+
540
+ const eventDetail: AnnotationCompletedEventDetail = {
541
+ annotation,
542
+ };
543
+
544
+ triggerEvent(eventTarget, eventType, eventDetail);
545
+ }
546
+
547
+ this.editData = null;
548
+ return annotation.annotationUID;
549
+ }
550
+ };
551
+
552
+ _activateModify = (element) => {
553
+ state.isInteractingWithTool = true;
554
+
555
+ element.addEventListener(Events.MOUSE_UP, this._endCallback);
556
+ element.addEventListener(Events.MOUSE_DRAG, this._dragModifyCallback);
557
+ element.addEventListener(Events.MOUSE_CLICK, this._endCallback);
558
+
559
+ element.addEventListener(Events.TOUCH_END, this._endCallback);
560
+ element.addEventListener(Events.TOUCH_DRAG, this._dragModifyCallback);
561
+ element.addEventListener(Events.TOUCH_TAP, this._endCallback);
562
+ };
563
+
564
+ _deactivateModify = (element) => {
565
+ state.isInteractingWithTool = false;
566
+
567
+ element.removeEventListener(Events.MOUSE_UP, this._endCallback);
568
+ element.removeEventListener(Events.MOUSE_DRAG, this._dragModifyCallback);
569
+ element.removeEventListener(Events.MOUSE_CLICK, this._endCallback);
570
+
571
+ element.removeEventListener(Events.TOUCH_END, this._endCallback);
572
+ element.removeEventListener(Events.TOUCH_DRAG, this._dragModifyCallback);
573
+ element.removeEventListener(Events.TOUCH_TAP, this._endCallback);
574
+ };
575
+
576
+ _activateDraw = (element) => {
577
+ state.isInteractingWithTool = true;
578
+
579
+ element.addEventListener(Events.MOUSE_UP, this._endCallback);
580
+ element.addEventListener(Events.MOUSE_DRAG, this._dragDrawCallback);
581
+ element.addEventListener(Events.MOUSE_MOVE, this._dragDrawCallback);
582
+ element.addEventListener(Events.MOUSE_CLICK, this._endCallback);
583
+
584
+ element.addEventListener(Events.TOUCH_END, this._endCallback);
585
+ element.addEventListener(Events.TOUCH_DRAG, this._dragDrawCallback);
586
+ element.addEventListener(Events.TOUCH_TAP, this._endCallback);
587
+ };
588
+
589
+ _deactivateDraw = (element) => {
590
+ state.isInteractingWithTool = false;
591
+
592
+ element.removeEventListener(Events.MOUSE_UP, this._endCallback);
593
+ element.removeEventListener(Events.MOUSE_DRAG, this._dragDrawCallback);
594
+ element.removeEventListener(Events.MOUSE_MOVE, this._dragDrawCallback);
595
+ element.removeEventListener(Events.MOUSE_CLICK, this._endCallback);
596
+
597
+ element.removeEventListener(Events.TOUCH_END, this._endCallback);
598
+ element.removeEventListener(Events.TOUCH_DRAG, this._dragDrawCallback);
599
+ element.removeEventListener(Events.TOUCH_TAP, this._endCallback);
600
+ };
601
+
602
+ /**
603
+ * it is used to draw the circleROI annotation in each
604
+ * request animation frame. It calculates the updated cached statistics if
605
+ * data is invalidated and cache it.
606
+ *
607
+ * @param enabledElement - The Cornerstone's enabledElement.
608
+ * @param svgDrawingHelper - The svgDrawingHelper providing the context for drawing.
609
+ */
610
+ renderAnnotation = (
611
+ enabledElement: Types.IEnabledElement,
612
+ svgDrawingHelper: SVGDrawingHelper
613
+ ): boolean => {
614
+ let renderStatus = false;
615
+ const { viewport } = enabledElement;
616
+ const { element } = viewport;
617
+
618
+ let annotations = getAnnotations(this.getToolName(), element);
619
+
620
+ if (!annotations?.length) {
621
+ return renderStatus;
622
+ }
623
+
624
+ annotations = this.filterInteractableAnnotationsForElement(
625
+ element,
626
+ annotations
627
+ );
628
+
629
+ if (!annotations?.length) {
630
+ return renderStatus;
631
+ }
632
+
633
+ const targetId = this.getTargetId(viewport);
634
+
635
+ const renderingEngine = viewport.getRenderingEngine();
636
+
637
+ const styleSpecifier: StyleSpecifier = {
638
+ toolGroupId: this.toolGroupId,
639
+ toolName: this.getToolName(),
640
+ viewportId: enabledElement.viewport.id,
641
+ };
642
+
643
+ for (let i = 0; i < annotations.length; i++) {
644
+ const annotation = annotations[i] as CircleROIAnnotation;
645
+ const { annotationUID, data } = annotation;
646
+ const { handles } = data;
647
+ const { points, activeHandleIndex } = handles;
648
+
649
+ styleSpecifier.annotationUID = annotationUID;
650
+
651
+ const lineWidth = this.getStyle('lineWidth', styleSpecifier, annotation);
652
+ const lineDash = this.getStyle('lineDash', styleSpecifier, annotation);
653
+ const color = this.getStyle('color', styleSpecifier, annotation);
654
+
655
+ const canvasCoordinates = points.map((p) =>
656
+ viewport.worldToCanvas(p)
657
+ ) as [Types.Point2, Types.Point2];
658
+ const center = canvasCoordinates[0];
659
+ const radius = getCanvasCircleRadius(canvasCoordinates);
660
+ const canvasCorners = getCanvasCircleCorners(canvasCoordinates);
661
+
662
+ const { centerPointRadius } = this.configuration;
663
+
664
+ // If cachedStats does not exist, or the unit is missing (as part of import/hydration etc.),
665
+ // force to recalculate the stats from the points
666
+ if (
667
+ !data.cachedStats[targetId] ||
668
+ data.cachedStats[targetId].areaUnit === undefined
669
+ ) {
670
+ data.cachedStats[targetId] = {
671
+ Modality: null,
672
+ area: null,
673
+ max: null,
674
+ mean: null,
675
+ stdDev: null,
676
+ areaUnit: null,
677
+ radius: null,
678
+ radiusUnit: null,
679
+ perimeter: null,
680
+ };
681
+
682
+ this._calculateCachedStats(
683
+ annotation,
684
+ viewport,
685
+ renderingEngine,
686
+ enabledElement
687
+ );
688
+ } else if (annotation.invalidated) {
689
+ this._throttledCalculateCachedStats(
690
+ annotation,
691
+ viewport,
692
+ renderingEngine,
693
+ enabledElement
694
+ );
695
+ // If the invalidated data is as a result of volumeViewport manipulation
696
+ // of the tools, we need to invalidate the related viewports data, so that
697
+ // when scrolling to the related slice in which the tool were manipulated
698
+ // we re-render the correct tool position. This is due to stackViewport
699
+ // which doesn't have the full volume at each time, and we are only working
700
+ // on one slice at a time.
701
+ if (viewport instanceof VolumeViewport) {
702
+ const { referencedImageId } = annotation.metadata;
703
+
704
+ // invalidate all the relevant stackViewports if they are not
705
+ // at the referencedImageId
706
+ for (const targetId in data.cachedStats) {
707
+ if (targetId.startsWith('imageId')) {
708
+ const viewports = renderingEngine.getStackViewports();
709
+
710
+ const invalidatedStack = viewports.find((vp) => {
711
+ // The stack viewport that contains the imageId but is not
712
+ // showing it currently
713
+ const referencedImageURI =
714
+ csUtils.imageIdToURI(referencedImageId);
715
+ const hasImageURI = vp.hasImageURI(referencedImageURI);
716
+ const currentImageURI = csUtils.imageIdToURI(
717
+ vp.getCurrentImageId()
718
+ );
719
+ return hasImageURI && currentImageURI !== referencedImageURI;
720
+ });
721
+
722
+ if (invalidatedStack) {
723
+ delete data.cachedStats[targetId];
724
+ }
725
+ }
726
+ }
727
+ }
728
+ }
729
+
730
+ // If rendering engine has been destroyed while rendering
731
+ if (!viewport.getRenderingEngine()) {
732
+ console.warn('Rendering Engine has been destroyed');
733
+ return renderStatus;
734
+ }
735
+
736
+ let activeHandleCanvasCoords;
737
+
738
+ if (!isAnnotationVisible(annotationUID)) {
739
+ continue;
740
+ }
741
+
742
+ if (
743
+ !isAnnotationLocked(annotation) &&
744
+ !this.editData &&
745
+ activeHandleIndex !== null
746
+ ) {
747
+ // Not locked or creating and hovering over handle, so render handle.
748
+ activeHandleCanvasCoords = [canvasCoordinates[activeHandleIndex]];
749
+ }
750
+
751
+ if (activeHandleCanvasCoords) {
752
+ const handleGroupUID = '0';
753
+ drawHandlesSvg(
754
+ svgDrawingHelper,
755
+ annotationUID,
756
+ handleGroupUID,
757
+ activeHandleCanvasCoords,
758
+ {
759
+ color,
760
+ }
761
+ );
762
+ }
763
+
764
+ const dataId = `${annotationUID}-circle`;
765
+ const circleUID = '0';
766
+ drawCircleSvg(
767
+ svgDrawingHelper,
768
+ annotationUID,
769
+ circleUID,
770
+ center,
771
+ radius,
772
+ {
773
+ color,
774
+ lineDash,
775
+ lineWidth,
776
+ },
777
+ dataId
778
+ );
779
+
780
+ // draw center point, if "centerPointRadius" configuration is valid.
781
+ if (centerPointRadius > 0) {
782
+ if (radius > 3 * centerPointRadius) {
783
+ drawCircleSvg(
784
+ svgDrawingHelper,
785
+ annotationUID,
786
+ `${circleUID}-center`,
787
+ center,
788
+ centerPointRadius,
789
+ {
790
+ color,
791
+ lineDash,
792
+ lineWidth,
793
+ }
794
+ );
795
+ }
796
+ }
797
+
798
+ renderStatus = true;
799
+
800
+ const isPreScaled = isViewportPreScaled(viewport, targetId);
801
+
802
+ const textLines = this._getTextLines(data, targetId, isPreScaled);
803
+ if (!textLines || textLines.length === 0) {
804
+ continue;
805
+ }
806
+
807
+ // Poor man's cached?
808
+ let canvasTextBoxCoords;
809
+
810
+ if (!data.handles.textBox.hasMoved) {
811
+ canvasTextBoxCoords = getTextBoxCoordsCanvas(canvasCorners);
812
+
813
+ data.handles.textBox.worldPosition =
814
+ viewport.canvasToWorld(canvasTextBoxCoords);
815
+ }
816
+
817
+ const textBoxPosition = viewport.worldToCanvas(
818
+ data.handles.textBox.worldPosition
819
+ );
820
+
821
+ const textBoxUID = '1';
822
+ const boundingBox = drawLinkedTextBoxSvg(
823
+ svgDrawingHelper,
824
+ annotationUID,
825
+ textBoxUID,
826
+ textLines,
827
+ textBoxPosition,
828
+ canvasCoordinates,
829
+ {},
830
+ this.getLinkedTextBoxStyle(styleSpecifier, annotation)
831
+ );
832
+
833
+ const { x: left, y: top, width, height } = boundingBox;
834
+
835
+ data.handles.textBox.worldBoundingBox = {
836
+ topLeft: viewport.canvasToWorld([left, top]),
837
+ topRight: viewport.canvasToWorld([left + width, top]),
838
+ bottomLeft: viewport.canvasToWorld([left, top + height]),
839
+ bottomRight: viewport.canvasToWorld([left + width, top + height]),
840
+ };
841
+ }
842
+
843
+ return renderStatus;
844
+ };
845
+
846
+ _getTextLines = (data, targetId: string, isPreScaled: boolean): string[] => {
847
+ const cachedVolumeStats = data.cachedStats[targetId];
848
+ const {
849
+ radius,
850
+ radiusUnit,
851
+ area,
852
+ mean,
853
+ stdDev,
854
+ max,
855
+ isEmptyArea,
856
+ Modality,
857
+ areaUnit,
858
+ } = cachedVolumeStats;
859
+
860
+ const textLines: string[] = [];
861
+ const unit = getModalityUnit(Modality, isPreScaled);
862
+
863
+ if (radius) {
864
+ const radiusLine = isEmptyArea
865
+ ? `Radius: Oblique not supported`
866
+ : `Radius: ${radius.toFixed(2)} ${radiusUnit}`;
867
+ textLines.push(radiusLine);
868
+ }
869
+
870
+ if (area) {
871
+ const areaLine = isEmptyArea
872
+ ? `Area: Oblique not supported`
873
+ : `Area: ${area.toFixed(2)} ${areaUnit}\xb2`;
874
+ textLines.push(areaLine);
875
+ }
876
+
877
+ if (mean) {
878
+ textLines.push(`Mean: ${mean.toFixed(2)} ${unit}`);
879
+ }
880
+
881
+ if (max) {
882
+ textLines.push(`Max: ${max.toFixed(2)} ${unit}`);
883
+ }
884
+
885
+ if (stdDev) {
886
+ textLines.push(`Std Dev: ${stdDev.toFixed(2)} ${unit}`);
887
+ }
888
+
889
+ return textLines;
890
+ };
891
+
892
+ _calculateCachedStats = (
893
+ annotation,
894
+ viewport,
895
+ renderingEngine,
896
+ enabledElement
897
+ ) => {
898
+ const data = annotation.data;
899
+ const { viewportId, renderingEngineId } = enabledElement;
900
+
901
+ const { points } = data.handles;
902
+
903
+ const canvasCoordinates = points.map((p) => viewport.worldToCanvas(p));
904
+ const { viewPlaneNormal, viewUp } = viewport.getCamera();
905
+
906
+ const [topLeftCanvas, bottomRightCanvas] = <Array<Types.Point2>>(
907
+ getCanvasCircleCorners(canvasCoordinates)
908
+ );
909
+
910
+ const topLeftWorld = viewport.canvasToWorld(topLeftCanvas);
911
+ const bottomRightWorld = viewport.canvasToWorld(bottomRightCanvas);
912
+ const { cachedStats } = data;
913
+
914
+ const targetIds = Object.keys(cachedStats);
915
+ const worldPos1 = topLeftWorld;
916
+ const worldPos2 = bottomRightWorld;
917
+
918
+ for (let i = 0; i < targetIds.length; i++) {
919
+ const targetId = targetIds[i];
920
+
921
+ const image = this.getTargetIdImage(targetId, renderingEngine);
922
+
923
+ // If image does not exists for the targetId, skip. This can be due
924
+ // to various reasons such as if the target was a volumeViewport, and
925
+ // the volumeViewport has been decached in the meantime.
926
+ if (!image) {
927
+ continue;
928
+ }
929
+
930
+ const { dimensions, imageData, metadata, hasPixelSpacing } = image;
931
+
932
+ const worldPos1Index = transformWorldToIndex(imageData, worldPos1);
933
+
934
+ worldPos1Index[0] = Math.floor(worldPos1Index[0]);
935
+ worldPos1Index[1] = Math.floor(worldPos1Index[1]);
936
+ worldPos1Index[2] = Math.floor(worldPos1Index[2]);
937
+
938
+ const worldPos2Index = transformWorldToIndex(imageData, worldPos2);
939
+
940
+ worldPos2Index[0] = Math.floor(worldPos2Index[0]);
941
+ worldPos2Index[1] = Math.floor(worldPos2Index[1]);
942
+ worldPos2Index[2] = Math.floor(worldPos2Index[2]);
943
+
944
+ // Check if one of the indexes are inside the volume, this then gives us
945
+ // Some area to do stats over.
946
+
947
+ if (this._isInsideVolume(worldPos1Index, worldPos2Index, dimensions)) {
948
+ const iMin = Math.min(worldPos1Index[0], worldPos2Index[0]);
949
+ const iMax = Math.max(worldPos1Index[0], worldPos2Index[0]);
950
+
951
+ const jMin = Math.min(worldPos1Index[1], worldPos2Index[1]);
952
+ const jMax = Math.max(worldPos1Index[1], worldPos2Index[1]);
953
+
954
+ const kMin = Math.min(worldPos1Index[2], worldPos2Index[2]);
955
+ const kMax = Math.max(worldPos1Index[2], worldPos2Index[2]);
956
+
957
+ const boundsIJK = [
958
+ [iMin, iMax],
959
+ [jMin, jMax],
960
+ [kMin, kMax],
961
+ ] as [Types.Point2, Types.Point2, Types.Point2];
962
+
963
+ const center = [
964
+ (topLeftWorld[0] + bottomRightWorld[0]) / 2,
965
+ (topLeftWorld[1] + bottomRightWorld[1]) / 2,
966
+ (topLeftWorld[2] + bottomRightWorld[2]) / 2,
967
+ ] as Types.Point3;
968
+
969
+ const ellipseObj = {
970
+ center,
971
+ xRadius: Math.abs(topLeftWorld[0] - bottomRightWorld[0]) / 2,
972
+ yRadius: Math.abs(topLeftWorld[1] - bottomRightWorld[1]) / 2,
973
+ zRadius: Math.abs(topLeftWorld[2] - bottomRightWorld[2]) / 2,
974
+ };
975
+
976
+ const { worldWidth, worldHeight } = getWorldWidthAndHeightFromTwoPoints(
977
+ viewPlaneNormal,
978
+ viewUp,
979
+ worldPos1,
980
+ worldPos2
981
+ );
982
+ const isEmptyArea = worldWidth === 0 && worldHeight === 0;
983
+ const area = Math.abs(Math.PI * (worldWidth / 2) * (worldHeight / 2));
984
+
985
+ let count = 0;
986
+ let mean = 0;
987
+ let stdDev = 0;
988
+ let max = -Infinity;
989
+
990
+ const meanMaxCalculator = ({ value: newValue }) => {
991
+ if (newValue > max) {
992
+ max = newValue;
993
+ }
994
+
995
+ mean += newValue;
996
+ count += 1;
997
+ };
998
+
999
+ pointInShapeCallback(
1000
+ imageData,
1001
+ (pointLPS, pointIJK) => pointInEllipse(ellipseObj, pointLPS),
1002
+ meanMaxCalculator,
1003
+ boundsIJK
1004
+ );
1005
+
1006
+ mean /= count;
1007
+
1008
+ const stdCalculator = ({ value }) => {
1009
+ const valueMinusMean = value - mean;
1010
+
1011
+ stdDev += valueMinusMean * valueMinusMean;
1012
+ };
1013
+
1014
+ pointInShapeCallback(
1015
+ imageData,
1016
+ (pointLPS, pointIJK) => pointInEllipse(ellipseObj, pointLPS),
1017
+ stdCalculator,
1018
+ boundsIJK
1019
+ );
1020
+
1021
+ stdDev /= count;
1022
+ stdDev = Math.sqrt(stdDev);
1023
+
1024
+ cachedStats[targetId] = {
1025
+ Modality: metadata.Modality,
1026
+ area,
1027
+ mean,
1028
+ max,
1029
+ stdDev,
1030
+ isEmptyArea,
1031
+ areaUnit: hasPixelSpacing ? 'mm' : 'px',
1032
+ radius: worldWidth / 2,
1033
+ radiusUnit: hasPixelSpacing ? 'mm' : 'px',
1034
+ perimeter: 2 * Math.PI * (worldWidth / 2),
1035
+ };
1036
+ } else {
1037
+ this.isHandleOutsideImage = true;
1038
+
1039
+ cachedStats[targetId] = {
1040
+ Modality: metadata.Modality,
1041
+ };
1042
+ }
1043
+ }
1044
+
1045
+ annotation.invalidated = false;
1046
+
1047
+ // Dispatching annotation modified
1048
+ const eventType = Events.ANNOTATION_MODIFIED;
1049
+
1050
+ const eventDetail: AnnotationModifiedEventDetail = {
1051
+ annotation,
1052
+ viewportId,
1053
+ renderingEngineId,
1054
+ };
1055
+
1056
+ triggerEvent(eventTarget, eventType, eventDetail);
1057
+
1058
+ return cachedStats;
1059
+ };
1060
+
1061
+ _isInsideVolume = (index1, index2, dimensions) => {
1062
+ return (
1063
+ csUtils.indexWithinDimensions(index1, dimensions) &&
1064
+ csUtils.indexWithinDimensions(index2, dimensions)
1065
+ );
1066
+ };
1067
+ }
1068
+
1069
+ CircleROITool.toolName = 'CircleROI';
1070
+ export default CircleROITool;