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