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