@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,2693 @@
|
|
|
1
|
+
import { vec2, vec3 } from 'gl-matrix';
|
|
2
|
+
import vtkMath from '@kitware/vtk.js/Common/Core/Math';
|
|
3
|
+
import vtkMatrixBuilder from '@kitware/vtk.js/Common/Core/MatrixBuilder';
|
|
4
|
+
|
|
5
|
+
import { AnnotationTool } from './base';
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
getEnabledElementByIds,
|
|
9
|
+
getEnabledElement,
|
|
10
|
+
utilities as csUtils,
|
|
11
|
+
Enums,
|
|
12
|
+
} from '@cornerstonejs/core';
|
|
13
|
+
import type { Types } from '@cornerstonejs/core';
|
|
14
|
+
|
|
15
|
+
import {
|
|
16
|
+
getToolGroup,
|
|
17
|
+
getToolGroupForViewport,
|
|
18
|
+
} from '../store/ToolGroupManager';
|
|
19
|
+
|
|
20
|
+
import {
|
|
21
|
+
addAnnotation,
|
|
22
|
+
getAnnotations,
|
|
23
|
+
removeAnnotation,
|
|
24
|
+
} from '../stateManagement/annotation/annotationState';
|
|
25
|
+
import {
|
|
26
|
+
drawCircle as drawCircleSvg,
|
|
27
|
+
drawHandles as drawHandlesSvg,
|
|
28
|
+
drawLine as drawLineSvg,
|
|
29
|
+
} from '../drawingSvg';
|
|
30
|
+
import { state } from '../store';
|
|
31
|
+
import { Events } from '../enums';
|
|
32
|
+
import { getViewportIdsWithToolToRender } from '../utilities/viewportFilters';
|
|
33
|
+
import {
|
|
34
|
+
resetElementCursor,
|
|
35
|
+
hideElementCursor,
|
|
36
|
+
} from '../cursors/elementCursor';
|
|
37
|
+
import liangBarksyClip from '../utilities/math/vec2/liangBarksyClip';
|
|
38
|
+
|
|
39
|
+
import * as lineSegment from '../utilities/math/line';
|
|
40
|
+
import {
|
|
41
|
+
Annotation,
|
|
42
|
+
Annotations,
|
|
43
|
+
EventTypes,
|
|
44
|
+
ToolHandle,
|
|
45
|
+
PublicToolProps,
|
|
46
|
+
ToolProps,
|
|
47
|
+
InteractionTypes,
|
|
48
|
+
SVGDrawingHelper,
|
|
49
|
+
} from '../types';
|
|
50
|
+
import { isAnnotationLocked } from '../stateManagement/annotation/annotationLocking';
|
|
51
|
+
import triggerAnnotationRenderForViewportIds from '../utilities/triggerAnnotationRenderForViewportIds';
|
|
52
|
+
import { CONSTANTS } from '@cornerstonejs/core';
|
|
53
|
+
|
|
54
|
+
const { RENDERING_DEFAULTS } = CONSTANTS;
|
|
55
|
+
|
|
56
|
+
// TODO: nested config is weird
|
|
57
|
+
interface ToolConfiguration {
|
|
58
|
+
configuration?: {
|
|
59
|
+
getReferenceLineColor?: (viewportId: string) => string;
|
|
60
|
+
getReferenceLineControllable?: (viewportId: string) => boolean;
|
|
61
|
+
getReferenceLineDraggableRotatable?: (viewportId: string) => boolean;
|
|
62
|
+
getReferenceLineSlabThicknessControlsOn?: (viewportId: string) => boolean;
|
|
63
|
+
shadow?: boolean;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
interface CrosshairsAnnotation extends Annotation {
|
|
68
|
+
data: {
|
|
69
|
+
handles: {
|
|
70
|
+
rotationPoints: any[]; // rotation handles, used for rotation interactions
|
|
71
|
+
slabThicknessPoints: any[]; // slab thickness handles, used for setting the slab thickness
|
|
72
|
+
activeOperation: number | null; // 0 translation, 1 rotation handles, 2 slab thickness handles
|
|
73
|
+
toolCenter: Types.Point3;
|
|
74
|
+
};
|
|
75
|
+
activeViewportIds: string[]; // a list of the viewport ids connected to the reference lines being translated
|
|
76
|
+
viewportId: string;
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function defaultReferenceLineColor() {
|
|
81
|
+
return 'rgb(0, 200, 0)';
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function defaultReferenceLineControllable() {
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function defaultReferenceLineDraggableRotatable() {
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function defaultReferenceLineSlabThicknessControlsOn() {
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const OPERATION = {
|
|
97
|
+
DRAG: 1,
|
|
98
|
+
ROTATE: 2,
|
|
99
|
+
SLAB: 3,
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const EPSILON = 1e-3;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* CrosshairsTool is a tool that provides reference lines between different viewports
|
|
106
|
+
* of a toolGroup. Using crosshairs, you can jump to a specific location in one
|
|
107
|
+
* viewport and the rest of the viewports in the toolGroup will be aligned to that location.
|
|
108
|
+
* Crosshairs have grababble handles that can be used to rotate and translate the
|
|
109
|
+
* reference lines. They can also be used to set the slab thickness of the viewports
|
|
110
|
+
* by modifying the slab thickness handles.
|
|
111
|
+
*
|
|
112
|
+
*/
|
|
113
|
+
class CrosshairsTool extends AnnotationTool {
|
|
114
|
+
static toolName;
|
|
115
|
+
|
|
116
|
+
// define the stroke width for mobile/web screen
|
|
117
|
+
private widthStrokeAnnotation = /Mobi|Android/i.test(navigator.userAgent)
|
|
118
|
+
? 2.5
|
|
119
|
+
: 1;
|
|
120
|
+
|
|
121
|
+
toolCenter: Types.Point3 = [0, 0, 0]; // NOTE: it is assumed that all the active/linked viewports share the same crosshair center.
|
|
122
|
+
// This because the rotation operation rotates also all the other active/intersecting reference lines of the same angle
|
|
123
|
+
_getReferenceLineColor?: (viewportId: string) => string;
|
|
124
|
+
_getReferenceLineControllable?: (viewportId: string) => boolean;
|
|
125
|
+
_getReferenceLineDraggableRotatable?: (viewportId: string) => boolean;
|
|
126
|
+
_getReferenceLineSlabThicknessControlsOn?: (viewportId: string) => boolean;
|
|
127
|
+
editData: {
|
|
128
|
+
annotation: any;
|
|
129
|
+
} | null;
|
|
130
|
+
|
|
131
|
+
constructor(
|
|
132
|
+
toolProps: PublicToolProps = {},
|
|
133
|
+
defaultToolProps: ToolProps = {
|
|
134
|
+
supportedInteractionTypes: ['Mouse'],
|
|
135
|
+
configuration: {
|
|
136
|
+
shadow: true,
|
|
137
|
+
// renders a colored circle on top right of the viewports whose color
|
|
138
|
+
// matches the color of the reference line
|
|
139
|
+
viewportIndicators: true,
|
|
140
|
+
// Auto pan is a configuration which will update pan
|
|
141
|
+
// other viewports in the toolGroup if the center of the crosshairs
|
|
142
|
+
// is outside of the viewport. This might be useful for the case
|
|
143
|
+
// when the user is scrolling through an image (usually in the zoomed view)
|
|
144
|
+
// and the crosshairs will eventually get outside of the viewport for
|
|
145
|
+
// the other viewports.
|
|
146
|
+
autoPan: {
|
|
147
|
+
enabled: false,
|
|
148
|
+
panSize: 10,
|
|
149
|
+
},
|
|
150
|
+
// actorUIDs for slabThickness application, if not defined, the slab thickness
|
|
151
|
+
// will be applied to all actors of the viewport
|
|
152
|
+
filterActorUIDsToSetSlabThickness: [],
|
|
153
|
+
// blend mode for slabThickness modifications
|
|
154
|
+
slabThicknessBlendMode: Enums.BlendModes.MAXIMUM_INTENSITY_BLEND,
|
|
155
|
+
},
|
|
156
|
+
}
|
|
157
|
+
) {
|
|
158
|
+
super(toolProps, defaultToolProps);
|
|
159
|
+
|
|
160
|
+
this._getReferenceLineColor =
|
|
161
|
+
toolProps.configuration?.getReferenceLineColor ||
|
|
162
|
+
defaultReferenceLineColor;
|
|
163
|
+
this._getReferenceLineControllable =
|
|
164
|
+
toolProps.configuration?.getReferenceLineControllable ||
|
|
165
|
+
defaultReferenceLineControllable;
|
|
166
|
+
this._getReferenceLineDraggableRotatable =
|
|
167
|
+
toolProps.configuration?.getReferenceLineDraggableRotatable ||
|
|
168
|
+
defaultReferenceLineDraggableRotatable;
|
|
169
|
+
this._getReferenceLineSlabThicknessControlsOn =
|
|
170
|
+
toolProps.configuration?.getReferenceLineSlabThicknessControlsOn ||
|
|
171
|
+
defaultReferenceLineSlabThicknessControlsOn;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Gets the camera from the viewport, and adds crosshairs annotation for the viewport
|
|
176
|
+
* to the annotationManager. If any annotation is found in the annotationManager, it
|
|
177
|
+
* overwrites it.
|
|
178
|
+
* @param viewportInfo - The viewportInfo for the viewport to add the crosshairs
|
|
179
|
+
* @returns viewPlaneNormal and center of viewport canvas in world space
|
|
180
|
+
*/
|
|
181
|
+
initializeViewport = ({
|
|
182
|
+
renderingEngineId,
|
|
183
|
+
viewportId,
|
|
184
|
+
}: Types.IViewportId): {
|
|
185
|
+
normal: Types.Point3;
|
|
186
|
+
point: Types.Point3;
|
|
187
|
+
} => {
|
|
188
|
+
const enabledElement = getEnabledElementByIds(
|
|
189
|
+
viewportId,
|
|
190
|
+
renderingEngineId
|
|
191
|
+
);
|
|
192
|
+
const { FrameOfReferenceUID, viewport } = enabledElement;
|
|
193
|
+
const { element } = viewport;
|
|
194
|
+
const { position, focalPoint, viewPlaneNormal } = viewport.getCamera();
|
|
195
|
+
|
|
196
|
+
// Check if there is already annotation for this viewport
|
|
197
|
+
let annotations = this._getAnnotations(enabledElement);
|
|
198
|
+
annotations = this.filterInteractableAnnotationsForElement(
|
|
199
|
+
element,
|
|
200
|
+
annotations
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
if (annotations.length) {
|
|
204
|
+
// If found, it will override it by removing the annotation and adding it later
|
|
205
|
+
removeAnnotation(annotations[0].annotationUID);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const annotation = {
|
|
209
|
+
highlighted: false,
|
|
210
|
+
metadata: {
|
|
211
|
+
cameraPosition: <Types.Point3>[...position],
|
|
212
|
+
cameraFocalPoint: <Types.Point3>[...focalPoint],
|
|
213
|
+
FrameOfReferenceUID,
|
|
214
|
+
toolName: this.getToolName(),
|
|
215
|
+
},
|
|
216
|
+
data: {
|
|
217
|
+
handles: {
|
|
218
|
+
rotationPoints: [], // rotation handles, used for rotation interactions
|
|
219
|
+
slabThicknessPoints: [], // slab thickness handles, used for setting the slab thickness
|
|
220
|
+
toolCenter: this.toolCenter,
|
|
221
|
+
},
|
|
222
|
+
activeOperation: null, // 0 translation, 1 rotation handles, 2 slab thickness handles
|
|
223
|
+
activeViewportIds: [], // a list of the viewport ids connected to the reference lines being translated
|
|
224
|
+
viewportId,
|
|
225
|
+
},
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
addAnnotation(annotation, element);
|
|
229
|
+
|
|
230
|
+
return {
|
|
231
|
+
normal: viewPlaneNormal,
|
|
232
|
+
point: viewport.canvasToWorld([
|
|
233
|
+
viewport.canvas.clientWidth / 2,
|
|
234
|
+
viewport.canvas.clientHeight / 2,
|
|
235
|
+
]),
|
|
236
|
+
};
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
_getViewportsInfo = () => {
|
|
240
|
+
const viewports = getToolGroup(this.toolGroupId).viewportsInfo;
|
|
241
|
+
|
|
242
|
+
return viewports;
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
onSetToolActive() {
|
|
246
|
+
const viewportsInfo = this._getViewportsInfo();
|
|
247
|
+
|
|
248
|
+
// Upon new setVolumes on viewports we need to update the crosshairs
|
|
249
|
+
// reference points in the new space, so we subscribe to the event
|
|
250
|
+
// and update the reference points accordingly.
|
|
251
|
+
this._unsubscribeToViewportNewVolumeSet(viewportsInfo);
|
|
252
|
+
this._subscribeToViewportNewVolumeSet(viewportsInfo);
|
|
253
|
+
|
|
254
|
+
this.computeToolCenter(viewportsInfo);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
onSetToolPassive() {
|
|
258
|
+
const viewportsInfo = this._getViewportsInfo();
|
|
259
|
+
|
|
260
|
+
this.computeToolCenter(viewportsInfo);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
onSetToolEnabled() {
|
|
264
|
+
const viewportsInfo = this._getViewportsInfo();
|
|
265
|
+
|
|
266
|
+
this.computeToolCenter(viewportsInfo);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
onSetToolDisabled() {
|
|
270
|
+
const viewportsInfo = this._getViewportsInfo();
|
|
271
|
+
|
|
272
|
+
this._unsubscribeToViewportNewVolumeSet(viewportsInfo);
|
|
273
|
+
|
|
274
|
+
// Crosshairs annotations in the state
|
|
275
|
+
// has no value when the tool is disabled
|
|
276
|
+
// since viewports can change (zoom, pan, scroll)
|
|
277
|
+
// between disabled and enabled/active states.
|
|
278
|
+
// so we just remove the annotations from the state
|
|
279
|
+
viewportsInfo.forEach(({ renderingEngineId, viewportId }) => {
|
|
280
|
+
const enabledElement = getEnabledElementByIds(
|
|
281
|
+
viewportId,
|
|
282
|
+
renderingEngineId
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
if (!enabledElement) {
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const annotations = this._getAnnotations(enabledElement);
|
|
290
|
+
|
|
291
|
+
if (annotations?.length) {
|
|
292
|
+
annotations.forEach((annotation) => {
|
|
293
|
+
removeAnnotation(annotation.annotationUID);
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* When activated, it initializes the crosshairs. It begins by computing
|
|
301
|
+
* the intersection of viewports associated with the crosshairs instance.
|
|
302
|
+
* When all three views are accessible, the intersection (e.g., crosshairs tool centre)
|
|
303
|
+
* will be an exact point in space; however, with two viewports, because the
|
|
304
|
+
* intersection of two planes is a line, it assumes the last view is between the centre
|
|
305
|
+
* of the two rendering viewports.
|
|
306
|
+
* @param viewportsInfo Array of viewportInputs which each item containing {viewportId, renderingEngineId}
|
|
307
|
+
*/
|
|
308
|
+
computeToolCenter = (viewportsInfo): void => {
|
|
309
|
+
if (!viewportsInfo.length || viewportsInfo.length === 1) {
|
|
310
|
+
throw new Error(
|
|
311
|
+
'For crosshairs to operate, at least two viewports must be given.'
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Todo: handle two same view viewport, or more than 3 viewports
|
|
316
|
+
const [firstViewport, secondViewport, thirdViewport] = viewportsInfo;
|
|
317
|
+
|
|
318
|
+
// Initialize first viewport
|
|
319
|
+
const { normal: normal1, point: point1 } =
|
|
320
|
+
this.initializeViewport(firstViewport);
|
|
321
|
+
|
|
322
|
+
// Initialize second viewport
|
|
323
|
+
const { normal: normal2, point: point2 } =
|
|
324
|
+
this.initializeViewport(secondViewport);
|
|
325
|
+
|
|
326
|
+
let normal3 = <Types.Point3>[0, 0, 0];
|
|
327
|
+
let point3 = vec3.create();
|
|
328
|
+
|
|
329
|
+
// If there are three viewports
|
|
330
|
+
if (thirdViewport) {
|
|
331
|
+
({ normal: normal3, point: point3 } =
|
|
332
|
+
this.initializeViewport(thirdViewport));
|
|
333
|
+
} else {
|
|
334
|
+
// If there are only two views (viewport) associated with the crosshairs:
|
|
335
|
+
// In this situation, we don't have a third information to find the
|
|
336
|
+
// exact intersection, and we "assume" the third view is looking at
|
|
337
|
+
// a location in between the first and second view centers
|
|
338
|
+
vec3.add(point3, point1, point2);
|
|
339
|
+
vec3.scale(point3, point3, 0.5);
|
|
340
|
+
vec3.cross(normal3, normal1, normal2);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Planes of each viewport
|
|
344
|
+
const firstPlane = csUtils.planar.planeEquation(normal1, point1);
|
|
345
|
+
const secondPlane = csUtils.planar.planeEquation(normal2, point2);
|
|
346
|
+
const thirdPlane = csUtils.planar.planeEquation(normal3, point3);
|
|
347
|
+
|
|
348
|
+
// Calculating the intersection of 3 planes
|
|
349
|
+
// prettier-ignore
|
|
350
|
+
this.toolCenter = csUtils.planar.threePlaneIntersection(firstPlane, secondPlane, thirdPlane)
|
|
351
|
+
|
|
352
|
+
// assuming all viewports are in the same rendering engine
|
|
353
|
+
const { renderingEngine } = getEnabledElementByIds(
|
|
354
|
+
viewportsInfo[0].viewportId,
|
|
355
|
+
viewportsInfo[0].renderingEngineId
|
|
356
|
+
);
|
|
357
|
+
|
|
358
|
+
triggerAnnotationRenderForViewportIds(
|
|
359
|
+
renderingEngine,
|
|
360
|
+
viewportsInfo.map(({ viewportId }) => viewportId)
|
|
361
|
+
);
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* addNewAnnotation acts as jump for the crosshairs tool. It is called when
|
|
366
|
+
* the user clicks on the image. It does not store the annotation in the stateManager though.
|
|
367
|
+
*
|
|
368
|
+
* @param evt - The mouse event
|
|
369
|
+
* @param interactionType - The type of interaction (e.g., mouse, touch, etc.)
|
|
370
|
+
* @returns Crosshairs annotation
|
|
371
|
+
*/
|
|
372
|
+
addNewAnnotation = (
|
|
373
|
+
evt: EventTypes.InteractionEventType
|
|
374
|
+
): CrosshairsAnnotation => {
|
|
375
|
+
const eventDetail = evt.detail;
|
|
376
|
+
const { element } = eventDetail;
|
|
377
|
+
|
|
378
|
+
const { currentPoints } = eventDetail;
|
|
379
|
+
const jumpWorld = currentPoints.world;
|
|
380
|
+
|
|
381
|
+
const enabledElement = getEnabledElement(element);
|
|
382
|
+
const { viewport } = enabledElement;
|
|
383
|
+
this._jump(enabledElement, jumpWorld);
|
|
384
|
+
|
|
385
|
+
const annotations = this._getAnnotations(enabledElement);
|
|
386
|
+
const filteredAnnotations = this.filterInteractableAnnotationsForElement(
|
|
387
|
+
viewport.element,
|
|
388
|
+
annotations
|
|
389
|
+
);
|
|
390
|
+
|
|
391
|
+
// viewport Annotation
|
|
392
|
+
const { data } = filteredAnnotations[0];
|
|
393
|
+
|
|
394
|
+
const { rotationPoints } = data.handles;
|
|
395
|
+
const viewportIdArray = [];
|
|
396
|
+
// put all the draggable reference lines in the viewportIdArray
|
|
397
|
+
for (let i = 0; i < rotationPoints.length - 1; ++i) {
|
|
398
|
+
const otherViewport = rotationPoints[i][1];
|
|
399
|
+
const viewportControllable = this._getReferenceLineControllable(
|
|
400
|
+
otherViewport.id
|
|
401
|
+
);
|
|
402
|
+
const viewportDraggableRotatable =
|
|
403
|
+
this._getReferenceLineDraggableRotatable(otherViewport.id);
|
|
404
|
+
if (!viewportControllable || !viewportDraggableRotatable) {
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
viewportIdArray.push(otherViewport.id);
|
|
408
|
+
// rotation handles are two per viewport
|
|
409
|
+
i++;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
data.activeViewportIds = [...viewportIdArray];
|
|
413
|
+
// set translation operation
|
|
414
|
+
data.handles.activeOperation = OPERATION.DRAG;
|
|
415
|
+
|
|
416
|
+
evt.preventDefault();
|
|
417
|
+
|
|
418
|
+
hideElementCursor(element);
|
|
419
|
+
|
|
420
|
+
this._activateModify(element);
|
|
421
|
+
return filteredAnnotations[0];
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
cancel = () => {
|
|
425
|
+
console.log('Not implemented yet');
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* It checks if the mouse click is near crosshairs handles, if yes
|
|
430
|
+
* it returns the handle location. If the mouse click is not near any
|
|
431
|
+
* of the handles, it does not return anything.
|
|
432
|
+
*
|
|
433
|
+
* @param element - The element that the tool is attached to.
|
|
434
|
+
* @param annotation - The annotation object associated with the annotation
|
|
435
|
+
* @param canvasCoords - The coordinates of the mouse click on canvas
|
|
436
|
+
* @param proximity - The distance from the mouse cursor to the point
|
|
437
|
+
* that is considered "near".
|
|
438
|
+
* @returns The handle that is closest to the cursor, or null if the cursor
|
|
439
|
+
* is not near any of the handles.
|
|
440
|
+
*/
|
|
441
|
+
getHandleNearImagePoint(
|
|
442
|
+
element: HTMLDivElement,
|
|
443
|
+
annotation: Annotation,
|
|
444
|
+
canvasCoords: Types.Point2,
|
|
445
|
+
proximity: number
|
|
446
|
+
): ToolHandle | undefined {
|
|
447
|
+
const enabledElement = getEnabledElement(element);
|
|
448
|
+
const { viewport } = enabledElement;
|
|
449
|
+
|
|
450
|
+
let point = this._getRotationHandleNearImagePoint(
|
|
451
|
+
viewport,
|
|
452
|
+
annotation,
|
|
453
|
+
canvasCoords,
|
|
454
|
+
proximity
|
|
455
|
+
);
|
|
456
|
+
|
|
457
|
+
if (point !== null) {
|
|
458
|
+
return point;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
point = this._getSlabThicknessHandleNearImagePoint(
|
|
462
|
+
viewport,
|
|
463
|
+
annotation,
|
|
464
|
+
canvasCoords,
|
|
465
|
+
proximity
|
|
466
|
+
);
|
|
467
|
+
|
|
468
|
+
if (point !== null) {
|
|
469
|
+
return point;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
handleSelectedCallback = (
|
|
474
|
+
evt: EventTypes.InteractionEventType,
|
|
475
|
+
annotation: Annotation
|
|
476
|
+
): void => {
|
|
477
|
+
const eventDetail = evt.detail;
|
|
478
|
+
const { element } = eventDetail;
|
|
479
|
+
|
|
480
|
+
annotation.highlighted = true;
|
|
481
|
+
|
|
482
|
+
// NOTE: handle index or coordinates are not used when dragging.
|
|
483
|
+
// This because the handle points are actually generated in the renderTool and they are a derivative
|
|
484
|
+
// from the camera variables of the viewports and of the slab thickness variable.
|
|
485
|
+
// Remember that the translation and rotation operations operate on the camera
|
|
486
|
+
// variables and not really on the handles. Similar for the slab thickness.
|
|
487
|
+
this._activateModify(element);
|
|
488
|
+
|
|
489
|
+
hideElementCursor(element);
|
|
490
|
+
|
|
491
|
+
evt.preventDefault();
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* It returns if the canvas point is near the provided crosshairs annotation in the
|
|
496
|
+
* provided element or not. A proximity is passed to the function to determine the
|
|
497
|
+
* proximity of the point to the annotation in number of pixels.
|
|
498
|
+
*
|
|
499
|
+
* @param element - HTML Element
|
|
500
|
+
* @param annotation - Annotation
|
|
501
|
+
* @param canvasCoords - Canvas coordinates
|
|
502
|
+
* @param proximity - Proximity to tool to consider
|
|
503
|
+
* @returns Boolean, whether the canvas point is near tool
|
|
504
|
+
*/
|
|
505
|
+
isPointNearTool = (
|
|
506
|
+
element: HTMLDivElement,
|
|
507
|
+
annotation: CrosshairsAnnotation,
|
|
508
|
+
canvasCoords: Types.Point2,
|
|
509
|
+
proximity: number
|
|
510
|
+
): boolean => {
|
|
511
|
+
if (this._pointNearTool(element, annotation, canvasCoords, 6)) {
|
|
512
|
+
return true;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
return false;
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
toolSelectedCallback = (
|
|
519
|
+
evt: EventTypes.InteractionEventType,
|
|
520
|
+
annotation: Annotation,
|
|
521
|
+
interactionType: InteractionTypes
|
|
522
|
+
): void => {
|
|
523
|
+
const eventDetail = evt.detail;
|
|
524
|
+
const { element } = eventDetail;
|
|
525
|
+
annotation.highlighted = true;
|
|
526
|
+
|
|
527
|
+
this._activateModify(element);
|
|
528
|
+
|
|
529
|
+
hideElementCursor(element);
|
|
530
|
+
|
|
531
|
+
evt.preventDefault();
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
onCameraModified = (evt) => {
|
|
535
|
+
const eventDetail = evt.detail;
|
|
536
|
+
const { element } = eventDetail;
|
|
537
|
+
const enabledElement = getEnabledElement(element);
|
|
538
|
+
const { renderingEngine } = enabledElement;
|
|
539
|
+
const viewport = enabledElement.viewport as Types.IVolumeViewport;
|
|
540
|
+
|
|
541
|
+
const annotations = this._getAnnotations(enabledElement);
|
|
542
|
+
const filteredToolAnnotations =
|
|
543
|
+
this.filterInteractableAnnotationsForElement(element, annotations);
|
|
544
|
+
|
|
545
|
+
// viewport that the camera modified is originating from
|
|
546
|
+
const viewportAnnotation =
|
|
547
|
+
filteredToolAnnotations[0] as CrosshairsAnnotation;
|
|
548
|
+
|
|
549
|
+
if (!viewportAnnotation) {
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// -- Update the camera of other linked viewports containing the same volumeId that
|
|
554
|
+
// have the same camera in case of translation
|
|
555
|
+
// -- Update the crosshair center in world coordinates in annotation.
|
|
556
|
+
// This is necessary because other tools can modify the position of the slices,
|
|
557
|
+
// e.g. stackScroll tool at wheel scroll. So we update the coordinates of the center always here.
|
|
558
|
+
// NOTE: rotation and slab thickness handles are created/updated in renderTool.
|
|
559
|
+
const currentCamera = viewport.getCamera();
|
|
560
|
+
const oldCameraPosition = viewportAnnotation.metadata.cameraPosition;
|
|
561
|
+
const deltaCameraPosition: Types.Point3 = [0, 0, 0];
|
|
562
|
+
vtkMath.subtract(
|
|
563
|
+
currentCamera.position,
|
|
564
|
+
oldCameraPosition,
|
|
565
|
+
deltaCameraPosition
|
|
566
|
+
);
|
|
567
|
+
|
|
568
|
+
const oldCameraFocalPoint = viewportAnnotation.metadata.cameraFocalPoint;
|
|
569
|
+
const deltaCameraFocalPoint: Types.Point3 = [0, 0, 0];
|
|
570
|
+
vtkMath.subtract(
|
|
571
|
+
currentCamera.focalPoint,
|
|
572
|
+
oldCameraFocalPoint,
|
|
573
|
+
deltaCameraFocalPoint
|
|
574
|
+
);
|
|
575
|
+
|
|
576
|
+
// updated cached "previous" camera position and focal point
|
|
577
|
+
viewportAnnotation.metadata.cameraPosition = [...currentCamera.position];
|
|
578
|
+
viewportAnnotation.metadata.cameraFocalPoint = [
|
|
579
|
+
...currentCamera.focalPoint,
|
|
580
|
+
];
|
|
581
|
+
|
|
582
|
+
const viewportControllable = this._getReferenceLineControllable(
|
|
583
|
+
viewport.id
|
|
584
|
+
);
|
|
585
|
+
const viewportDraggableRotatable = this._getReferenceLineDraggableRotatable(
|
|
586
|
+
viewport.id
|
|
587
|
+
);
|
|
588
|
+
if (
|
|
589
|
+
!csUtils.isEqual(currentCamera.position, oldCameraPosition, 1e-3) &&
|
|
590
|
+
viewportControllable &&
|
|
591
|
+
viewportDraggableRotatable
|
|
592
|
+
) {
|
|
593
|
+
// Is camera Modified a TRANSLATION or ROTATION?
|
|
594
|
+
let isRotation = false;
|
|
595
|
+
|
|
596
|
+
// This is guaranteed to be the same diff for both position and focal point
|
|
597
|
+
// if the camera is modified by pan, zoom, or scroll BUT for rotation of
|
|
598
|
+
// crosshairs handles it will be different.
|
|
599
|
+
const cameraModifiedSameForPosAndFocalPoint = csUtils.isEqual(
|
|
600
|
+
deltaCameraPosition,
|
|
601
|
+
deltaCameraFocalPoint,
|
|
602
|
+
1e-3
|
|
603
|
+
);
|
|
604
|
+
|
|
605
|
+
// NOTE: it is a translation if the the focal point and camera position shifts are the same
|
|
606
|
+
if (!cameraModifiedSameForPosAndFocalPoint) {
|
|
607
|
+
isRotation = true;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
const cameraModifiedInPlane =
|
|
611
|
+
Math.abs(
|
|
612
|
+
vtkMath.dot(deltaCameraPosition, currentCamera.viewPlaneNormal)
|
|
613
|
+
) < 1e-2;
|
|
614
|
+
|
|
615
|
+
// TRANSLATION
|
|
616
|
+
// NOTE1: if the camera modified is a result of a pan or zoom don't update the crosshair center
|
|
617
|
+
// NOTE2: rotation handles are updates in renderTool
|
|
618
|
+
if (!isRotation && !cameraModifiedInPlane) {
|
|
619
|
+
this.toolCenter[0] += deltaCameraPosition[0];
|
|
620
|
+
this.toolCenter[1] += deltaCameraPosition[1];
|
|
621
|
+
this.toolCenter[2] += deltaCameraPosition[2];
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
// AutoPan modification
|
|
626
|
+
if (this.configuration.autoPan?.enabled) {
|
|
627
|
+
const toolGroup = getToolGroupForViewport(
|
|
628
|
+
viewport.id,
|
|
629
|
+
renderingEngine.id
|
|
630
|
+
);
|
|
631
|
+
|
|
632
|
+
const otherViewportIds = toolGroup
|
|
633
|
+
.getViewportIds()
|
|
634
|
+
.filter((id) => id !== viewport.id);
|
|
635
|
+
|
|
636
|
+
otherViewportIds.forEach((viewportId) => {
|
|
637
|
+
this._autoPanViewportIfNecessary(viewportId, renderingEngine);
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
const requireSameOrientation = false;
|
|
642
|
+
const viewportIdsToRender = getViewportIdsWithToolToRender(
|
|
643
|
+
element,
|
|
644
|
+
this.getToolName(),
|
|
645
|
+
requireSameOrientation
|
|
646
|
+
);
|
|
647
|
+
|
|
648
|
+
triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender);
|
|
649
|
+
};
|
|
650
|
+
|
|
651
|
+
mouseMoveCallback = (
|
|
652
|
+
evt: EventTypes.MouseMoveEventType,
|
|
653
|
+
filteredToolAnnotations: Annotations
|
|
654
|
+
): boolean => {
|
|
655
|
+
const { element, currentPoints } = evt.detail;
|
|
656
|
+
const canvasCoords = currentPoints.canvas;
|
|
657
|
+
let imageNeedsUpdate = false;
|
|
658
|
+
|
|
659
|
+
for (let i = 0; i < filteredToolAnnotations.length; i++) {
|
|
660
|
+
const annotation = filteredToolAnnotations[i] as CrosshairsAnnotation;
|
|
661
|
+
|
|
662
|
+
if (isAnnotationLocked(annotation)) {
|
|
663
|
+
continue;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
const { data, highlighted } = annotation;
|
|
667
|
+
if (!data.handles) {
|
|
668
|
+
continue;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
const previousActiveOperation = data.handles.activeOperation;
|
|
672
|
+
const previousActiveViewportIds =
|
|
673
|
+
data.activeViewportIds && data.activeViewportIds.length > 0
|
|
674
|
+
? [...data.activeViewportIds]
|
|
675
|
+
: [];
|
|
676
|
+
|
|
677
|
+
// This init are necessary, because when we move the mouse they are not cleaned by _endCallback
|
|
678
|
+
data.activeViewportIds = [];
|
|
679
|
+
data.handles.activeOperation = null;
|
|
680
|
+
|
|
681
|
+
const handleNearImagePoint = this.getHandleNearImagePoint(
|
|
682
|
+
element,
|
|
683
|
+
annotation,
|
|
684
|
+
canvasCoords,
|
|
685
|
+
6
|
|
686
|
+
);
|
|
687
|
+
|
|
688
|
+
let near = false;
|
|
689
|
+
if (handleNearImagePoint) {
|
|
690
|
+
near = true;
|
|
691
|
+
} else {
|
|
692
|
+
near = this._pointNearTool(element, annotation, canvasCoords, 6);
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
const nearToolAndNotMarkedActive = near && !highlighted;
|
|
696
|
+
const notNearToolAndMarkedActive = !near && highlighted;
|
|
697
|
+
if (nearToolAndNotMarkedActive || notNearToolAndMarkedActive) {
|
|
698
|
+
annotation.highlighted = !highlighted;
|
|
699
|
+
imageNeedsUpdate = true;
|
|
700
|
+
} else if (
|
|
701
|
+
data.handles.activeOperation !== previousActiveOperation ||
|
|
702
|
+
!this._areViewportIdArraysEqual(
|
|
703
|
+
data.activeViewportIds,
|
|
704
|
+
previousActiveViewportIds
|
|
705
|
+
)
|
|
706
|
+
) {
|
|
707
|
+
imageNeedsUpdate = true;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
return imageNeedsUpdate;
|
|
712
|
+
};
|
|
713
|
+
|
|
714
|
+
filterInteractableAnnotationsForElement = (element, annotations) => {
|
|
715
|
+
if (!annotations || !annotations.length) {
|
|
716
|
+
return [];
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
const enabledElement = getEnabledElement(element);
|
|
720
|
+
const { viewportId } = enabledElement;
|
|
721
|
+
|
|
722
|
+
const viewportUIDSpecificCrosshairs = annotations.filter(
|
|
723
|
+
(annotation) => annotation.data.viewportId === viewportId
|
|
724
|
+
);
|
|
725
|
+
|
|
726
|
+
return viewportUIDSpecificCrosshairs;
|
|
727
|
+
};
|
|
728
|
+
|
|
729
|
+
/**
|
|
730
|
+
* renders the crosshairs lines and handles in the requestAnimationFrame callback
|
|
731
|
+
*
|
|
732
|
+
* @param enabledElement - The Cornerstone's enabledElement.
|
|
733
|
+
* @param svgDrawingHelper - The svgDrawingHelper providing the context for drawing.
|
|
734
|
+
*/
|
|
735
|
+
renderAnnotation = (
|
|
736
|
+
enabledElement: Types.IEnabledElement,
|
|
737
|
+
svgDrawingHelper: SVGDrawingHelper
|
|
738
|
+
): boolean => {
|
|
739
|
+
let renderStatus = false;
|
|
740
|
+
const { viewport, renderingEngine } = enabledElement;
|
|
741
|
+
const { element } = viewport;
|
|
742
|
+
const annotations = this._getAnnotations(enabledElement);
|
|
743
|
+
const camera = viewport.getCamera();
|
|
744
|
+
const filteredToolAnnotations =
|
|
745
|
+
this.filterInteractableAnnotationsForElement(element, annotations);
|
|
746
|
+
|
|
747
|
+
// viewport Annotation
|
|
748
|
+
const viewportAnnotation = filteredToolAnnotations[0];
|
|
749
|
+
if (!annotations?.length || !viewportAnnotation?.data) {
|
|
750
|
+
// No annotations yet, and didn't just create it as we likely don't have a FrameOfReference/any data loaded yet.
|
|
751
|
+
return renderStatus;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
const annotationUID = viewportAnnotation.annotationUID;
|
|
755
|
+
|
|
756
|
+
// Get cameras/canvases for each of these.
|
|
757
|
+
// -- Get two world positions for this canvas in this line (e.g. the diagonal)
|
|
758
|
+
// -- Convert these world positions to this canvas.
|
|
759
|
+
// -- Extend/confine this line to fit in this canvas.
|
|
760
|
+
// -- Render this line.
|
|
761
|
+
const { clientWidth, clientHeight } = viewport.canvas;
|
|
762
|
+
const canvasDiagonalLength = Math.sqrt(
|
|
763
|
+
clientWidth * clientWidth + clientHeight * clientHeight
|
|
764
|
+
);
|
|
765
|
+
|
|
766
|
+
const data = viewportAnnotation.data;
|
|
767
|
+
const crosshairCenterCanvas = viewport.worldToCanvas(this.toolCenter);
|
|
768
|
+
|
|
769
|
+
const otherViewportAnnotations =
|
|
770
|
+
this._filterAnnotationsByUniqueViewportOrientations(
|
|
771
|
+
enabledElement,
|
|
772
|
+
annotations
|
|
773
|
+
);
|
|
774
|
+
|
|
775
|
+
const referenceLines = [];
|
|
776
|
+
|
|
777
|
+
otherViewportAnnotations.forEach((annotation) => {
|
|
778
|
+
const { data } = annotation;
|
|
779
|
+
|
|
780
|
+
data.handles.toolCenter = this.toolCenter;
|
|
781
|
+
|
|
782
|
+
const otherViewport = renderingEngine.getViewport(
|
|
783
|
+
data.viewportId
|
|
784
|
+
) as Types.IVolumeViewport;
|
|
785
|
+
|
|
786
|
+
const otherCamera = otherViewport.getCamera();
|
|
787
|
+
|
|
788
|
+
const otherViewportControllable = this._getReferenceLineControllable(
|
|
789
|
+
otherViewport.id
|
|
790
|
+
);
|
|
791
|
+
const otherViewportDraggableRotatable =
|
|
792
|
+
this._getReferenceLineDraggableRotatable(otherViewport.id);
|
|
793
|
+
const otherViewportSlabThicknessControlsOn =
|
|
794
|
+
this._getReferenceLineSlabThicknessControlsOn(otherViewport.id);
|
|
795
|
+
|
|
796
|
+
// get coordinates for the reference line
|
|
797
|
+
const { clientWidth, clientHeight } = otherViewport.canvas;
|
|
798
|
+
const otherCanvasDiagonalLength = Math.sqrt(
|
|
799
|
+
clientWidth * clientWidth + clientHeight * clientHeight
|
|
800
|
+
);
|
|
801
|
+
const otherCanvasCenter: Types.Point2 = [
|
|
802
|
+
clientWidth * 0.5,
|
|
803
|
+
clientHeight * 0.5,
|
|
804
|
+
];
|
|
805
|
+
const otherViewportCenterWorld =
|
|
806
|
+
otherViewport.canvasToWorld(otherCanvasCenter);
|
|
807
|
+
|
|
808
|
+
const direction: Types.Point3 = [0, 0, 0];
|
|
809
|
+
vtkMath.cross(
|
|
810
|
+
camera.viewPlaneNormal,
|
|
811
|
+
otherCamera.viewPlaneNormal,
|
|
812
|
+
direction
|
|
813
|
+
);
|
|
814
|
+
vtkMath.normalize(direction);
|
|
815
|
+
vtkMath.multiplyScalar(
|
|
816
|
+
<Types.Point3>direction,
|
|
817
|
+
otherCanvasDiagonalLength
|
|
818
|
+
);
|
|
819
|
+
|
|
820
|
+
const pointWorld0: Types.Point3 = [0, 0, 0];
|
|
821
|
+
vtkMath.add(otherViewportCenterWorld, direction, pointWorld0);
|
|
822
|
+
|
|
823
|
+
const pointWorld1: Types.Point3 = [0, 0, 0];
|
|
824
|
+
vtkMath.subtract(otherViewportCenterWorld, direction, pointWorld1);
|
|
825
|
+
|
|
826
|
+
// get canvas information for points and lines (canvas box, canvas horizontal distances)
|
|
827
|
+
const canvasBox = [0, 0, clientWidth, clientHeight];
|
|
828
|
+
|
|
829
|
+
const pointCanvas0 = viewport.worldToCanvas(pointWorld0);
|
|
830
|
+
|
|
831
|
+
const otherViewportCenterCanvas = viewport.worldToCanvas(
|
|
832
|
+
otherViewportCenterWorld
|
|
833
|
+
);
|
|
834
|
+
|
|
835
|
+
const canvasUnitVectorFromCenter = vec2.create();
|
|
836
|
+
vec2.subtract(
|
|
837
|
+
canvasUnitVectorFromCenter,
|
|
838
|
+
pointCanvas0,
|
|
839
|
+
otherViewportCenterCanvas
|
|
840
|
+
);
|
|
841
|
+
vec2.normalize(canvasUnitVectorFromCenter, canvasUnitVectorFromCenter);
|
|
842
|
+
|
|
843
|
+
// Graphic:
|
|
844
|
+
// Mid -> SlabThickness handle
|
|
845
|
+
// Short -> Rotation handle
|
|
846
|
+
// Long
|
|
847
|
+
// |
|
|
848
|
+
// |
|
|
849
|
+
// |
|
|
850
|
+
// Mid
|
|
851
|
+
// |
|
|
852
|
+
// |
|
|
853
|
+
// |
|
|
854
|
+
// Short
|
|
855
|
+
// |
|
|
856
|
+
// |
|
|
857
|
+
// |
|
|
858
|
+
// Long --- Mid--- Short--- Center --- Short --- Mid --- Long
|
|
859
|
+
// |
|
|
860
|
+
// |
|
|
861
|
+
// |
|
|
862
|
+
// Short
|
|
863
|
+
// |
|
|
864
|
+
// |
|
|
865
|
+
// |
|
|
866
|
+
// Mid
|
|
867
|
+
// |
|
|
868
|
+
// |
|
|
869
|
+
// |
|
|
870
|
+
// Long
|
|
871
|
+
const canvasVectorFromCenterLong = vec2.create();
|
|
872
|
+
|
|
873
|
+
// Todo: configuration should provide constants below (100, 0.25, 0.15, 0.04)
|
|
874
|
+
vec2.scale(
|
|
875
|
+
canvasVectorFromCenterLong,
|
|
876
|
+
canvasUnitVectorFromCenter,
|
|
877
|
+
canvasDiagonalLength * 100
|
|
878
|
+
);
|
|
879
|
+
const canvasVectorFromCenterMid = vec2.create();
|
|
880
|
+
vec2.scale(
|
|
881
|
+
canvasVectorFromCenterMid,
|
|
882
|
+
canvasUnitVectorFromCenter,
|
|
883
|
+
canvasDiagonalLength * 0.25
|
|
884
|
+
);
|
|
885
|
+
const canvasVectorFromCenterShort = vec2.create();
|
|
886
|
+
vec2.scale(
|
|
887
|
+
canvasVectorFromCenterShort,
|
|
888
|
+
canvasUnitVectorFromCenter,
|
|
889
|
+
canvasDiagonalLength * 0.15
|
|
890
|
+
);
|
|
891
|
+
const canvasVectorFromCenterStart = vec2.create();
|
|
892
|
+
vec2.scale(
|
|
893
|
+
canvasVectorFromCenterStart,
|
|
894
|
+
canvasUnitVectorFromCenter,
|
|
895
|
+
// Don't put a gap if the the third view is missing
|
|
896
|
+
otherViewportAnnotations.length === 2 ? canvasDiagonalLength * 0.04 : 0
|
|
897
|
+
);
|
|
898
|
+
|
|
899
|
+
// Computing Reference start and end (4 lines per viewport in case of 3 view MPR)
|
|
900
|
+
const refLinePointOne = vec2.create();
|
|
901
|
+
const refLinePointTwo = vec2.create();
|
|
902
|
+
const refLinePointThree = vec2.create();
|
|
903
|
+
const refLinePointFour = vec2.create();
|
|
904
|
+
|
|
905
|
+
let refLinesCenter = vec2.clone(crosshairCenterCanvas);
|
|
906
|
+
if (!otherViewportDraggableRotatable || !otherViewportControllable) {
|
|
907
|
+
refLinesCenter = vec2.clone(otherViewportCenterCanvas);
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
vec2.add(refLinePointOne, refLinesCenter, canvasVectorFromCenterStart);
|
|
911
|
+
vec2.add(refLinePointTwo, refLinesCenter, canvasVectorFromCenterLong);
|
|
912
|
+
vec2.subtract(
|
|
913
|
+
refLinePointThree,
|
|
914
|
+
refLinesCenter,
|
|
915
|
+
canvasVectorFromCenterStart
|
|
916
|
+
);
|
|
917
|
+
vec2.subtract(
|
|
918
|
+
refLinePointFour,
|
|
919
|
+
refLinesCenter,
|
|
920
|
+
canvasVectorFromCenterLong
|
|
921
|
+
);
|
|
922
|
+
|
|
923
|
+
// Clipping lines to be only included in a box (canvas), we don't want
|
|
924
|
+
// the lines goes beyond canvas
|
|
925
|
+
liangBarksyClip(refLinePointOne, refLinePointTwo, canvasBox);
|
|
926
|
+
liangBarksyClip(refLinePointThree, refLinePointFour, canvasBox);
|
|
927
|
+
|
|
928
|
+
// Computing rotation handle positions
|
|
929
|
+
const rotHandleOne = vec2.create();
|
|
930
|
+
vec2.subtract(
|
|
931
|
+
rotHandleOne,
|
|
932
|
+
crosshairCenterCanvas,
|
|
933
|
+
canvasVectorFromCenterMid
|
|
934
|
+
);
|
|
935
|
+
|
|
936
|
+
const rotHandleTwo = vec2.create();
|
|
937
|
+
vec2.add(rotHandleTwo, crosshairCenterCanvas, canvasVectorFromCenterMid);
|
|
938
|
+
|
|
939
|
+
// Computing SlabThickness (st below) position
|
|
940
|
+
|
|
941
|
+
// SlabThickness center in canvas
|
|
942
|
+
let stHandlesCenterCanvas = vec2.clone(crosshairCenterCanvas);
|
|
943
|
+
if (
|
|
944
|
+
!otherViewportDraggableRotatable &&
|
|
945
|
+
otherViewportSlabThicknessControlsOn
|
|
946
|
+
) {
|
|
947
|
+
stHandlesCenterCanvas = vec2.clone(otherViewportCenterCanvas);
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
// SlabThickness center in world
|
|
951
|
+
let stHandlesCenterWorld: Types.Point3 = [...this.toolCenter];
|
|
952
|
+
if (
|
|
953
|
+
!otherViewportDraggableRotatable &&
|
|
954
|
+
otherViewportSlabThicknessControlsOn
|
|
955
|
+
) {
|
|
956
|
+
stHandlesCenterWorld = [...otherViewportCenterWorld];
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
const worldUnitVectorFromCenter: Types.Point3 = [0, 0, 0];
|
|
960
|
+
vtkMath.subtract(pointWorld0, pointWorld1, worldUnitVectorFromCenter);
|
|
961
|
+
vtkMath.normalize(worldUnitVectorFromCenter);
|
|
962
|
+
|
|
963
|
+
const { viewPlaneNormal } = camera;
|
|
964
|
+
// @ts-ignore // Todo: fix after vtk pr merged
|
|
965
|
+
const { matrix } = vtkMatrixBuilder
|
|
966
|
+
.buildFromDegree()
|
|
967
|
+
// @ts-ignore fix after vtk pr merged
|
|
968
|
+
.rotate(90, viewPlaneNormal);
|
|
969
|
+
|
|
970
|
+
const worldUnitOrthoVectorFromCenter: Types.Point3 = [0, 0, 0];
|
|
971
|
+
vec3.transformMat4(
|
|
972
|
+
worldUnitOrthoVectorFromCenter,
|
|
973
|
+
worldUnitVectorFromCenter,
|
|
974
|
+
matrix
|
|
975
|
+
);
|
|
976
|
+
|
|
977
|
+
const slabThicknessValue = otherViewport.getSlabThickness();
|
|
978
|
+
const worldOrthoVectorFromCenter: Types.Point3 = [
|
|
979
|
+
...worldUnitOrthoVectorFromCenter,
|
|
980
|
+
];
|
|
981
|
+
vtkMath.multiplyScalar(worldOrthoVectorFromCenter, slabThicknessValue);
|
|
982
|
+
|
|
983
|
+
const worldVerticalRefPoint: Types.Point3 = [0, 0, 0];
|
|
984
|
+
vtkMath.add(
|
|
985
|
+
stHandlesCenterWorld,
|
|
986
|
+
worldOrthoVectorFromCenter,
|
|
987
|
+
worldVerticalRefPoint
|
|
988
|
+
);
|
|
989
|
+
|
|
990
|
+
// convert vertical world distances in canvas coordinates
|
|
991
|
+
const canvasVerticalRefPoint = viewport.worldToCanvas(
|
|
992
|
+
worldVerticalRefPoint
|
|
993
|
+
);
|
|
994
|
+
|
|
995
|
+
// points for slab thickness lines
|
|
996
|
+
const canvasOrthoVectorFromCenter = vec2.create();
|
|
997
|
+
vec2.subtract(
|
|
998
|
+
canvasOrthoVectorFromCenter,
|
|
999
|
+
stHandlesCenterCanvas,
|
|
1000
|
+
canvasVerticalRefPoint
|
|
1001
|
+
);
|
|
1002
|
+
|
|
1003
|
+
const stLinePointOne = vec2.create();
|
|
1004
|
+
vec2.subtract(
|
|
1005
|
+
stLinePointOne,
|
|
1006
|
+
stHandlesCenterCanvas,
|
|
1007
|
+
canvasVectorFromCenterLong
|
|
1008
|
+
);
|
|
1009
|
+
vec2.add(stLinePointOne, stLinePointOne, canvasOrthoVectorFromCenter);
|
|
1010
|
+
|
|
1011
|
+
const stLinePointTwo = vec2.create();
|
|
1012
|
+
vec2.add(
|
|
1013
|
+
stLinePointTwo,
|
|
1014
|
+
stHandlesCenterCanvas,
|
|
1015
|
+
canvasVectorFromCenterLong
|
|
1016
|
+
);
|
|
1017
|
+
vec2.add(stLinePointTwo, stLinePointTwo, canvasOrthoVectorFromCenter);
|
|
1018
|
+
|
|
1019
|
+
liangBarksyClip(stLinePointOne, stLinePointTwo, canvasBox);
|
|
1020
|
+
|
|
1021
|
+
const stLinePointThree = vec2.create();
|
|
1022
|
+
vec2.add(
|
|
1023
|
+
stLinePointThree,
|
|
1024
|
+
stHandlesCenterCanvas,
|
|
1025
|
+
canvasVectorFromCenterLong
|
|
1026
|
+
);
|
|
1027
|
+
vec2.subtract(
|
|
1028
|
+
stLinePointThree,
|
|
1029
|
+
stLinePointThree,
|
|
1030
|
+
canvasOrthoVectorFromCenter
|
|
1031
|
+
);
|
|
1032
|
+
|
|
1033
|
+
const stLinePointFour = vec2.create();
|
|
1034
|
+
vec2.subtract(
|
|
1035
|
+
stLinePointFour,
|
|
1036
|
+
stHandlesCenterCanvas,
|
|
1037
|
+
canvasVectorFromCenterLong
|
|
1038
|
+
);
|
|
1039
|
+
vec2.subtract(
|
|
1040
|
+
stLinePointFour,
|
|
1041
|
+
stLinePointFour,
|
|
1042
|
+
canvasOrthoVectorFromCenter
|
|
1043
|
+
);
|
|
1044
|
+
|
|
1045
|
+
liangBarksyClip(stLinePointThree, stLinePointFour, canvasBox);
|
|
1046
|
+
|
|
1047
|
+
// points for slab thickness handles
|
|
1048
|
+
const stHandleOne = vec2.create();
|
|
1049
|
+
const stHandleTwo = vec2.create();
|
|
1050
|
+
const stHandleThree = vec2.create();
|
|
1051
|
+
const stHandleFour = vec2.create();
|
|
1052
|
+
|
|
1053
|
+
vec2.subtract(
|
|
1054
|
+
stHandleOne,
|
|
1055
|
+
stHandlesCenterCanvas,
|
|
1056
|
+
canvasVectorFromCenterShort
|
|
1057
|
+
);
|
|
1058
|
+
vec2.add(stHandleOne, stHandleOne, canvasOrthoVectorFromCenter);
|
|
1059
|
+
vec2.add(stHandleTwo, stHandlesCenterCanvas, canvasVectorFromCenterShort);
|
|
1060
|
+
vec2.add(stHandleTwo, stHandleTwo, canvasOrthoVectorFromCenter);
|
|
1061
|
+
vec2.subtract(
|
|
1062
|
+
stHandleThree,
|
|
1063
|
+
stHandlesCenterCanvas,
|
|
1064
|
+
canvasVectorFromCenterShort
|
|
1065
|
+
);
|
|
1066
|
+
vec2.subtract(stHandleThree, stHandleThree, canvasOrthoVectorFromCenter);
|
|
1067
|
+
vec2.add(
|
|
1068
|
+
stHandleFour,
|
|
1069
|
+
stHandlesCenterCanvas,
|
|
1070
|
+
canvasVectorFromCenterShort
|
|
1071
|
+
);
|
|
1072
|
+
vec2.subtract(stHandleFour, stHandleFour, canvasOrthoVectorFromCenter);
|
|
1073
|
+
|
|
1074
|
+
referenceLines.push([
|
|
1075
|
+
otherViewport,
|
|
1076
|
+
refLinePointOne,
|
|
1077
|
+
refLinePointTwo,
|
|
1078
|
+
refLinePointThree,
|
|
1079
|
+
refLinePointFour,
|
|
1080
|
+
stLinePointOne,
|
|
1081
|
+
stLinePointTwo,
|
|
1082
|
+
stLinePointThree,
|
|
1083
|
+
stLinePointFour,
|
|
1084
|
+
rotHandleOne,
|
|
1085
|
+
rotHandleTwo,
|
|
1086
|
+
stHandleOne,
|
|
1087
|
+
stHandleTwo,
|
|
1088
|
+
stHandleThree,
|
|
1089
|
+
stHandleFour,
|
|
1090
|
+
]);
|
|
1091
|
+
});
|
|
1092
|
+
|
|
1093
|
+
const newRtpoints = [];
|
|
1094
|
+
const newStpoints = [];
|
|
1095
|
+
const viewportColor = this._getReferenceLineColor(viewport.id);
|
|
1096
|
+
const color =
|
|
1097
|
+
viewportColor !== undefined ? viewportColor : 'rgb(200, 200, 200)';
|
|
1098
|
+
|
|
1099
|
+
referenceLines.forEach((line, lineIndex) => {
|
|
1100
|
+
// get color for the reference line
|
|
1101
|
+
const otherViewport = line[0];
|
|
1102
|
+
const viewportColor = this._getReferenceLineColor(otherViewport.id);
|
|
1103
|
+
const viewportControllable = this._getReferenceLineControllable(
|
|
1104
|
+
otherViewport.id
|
|
1105
|
+
);
|
|
1106
|
+
const viewportDraggableRotatable =
|
|
1107
|
+
this._getReferenceLineDraggableRotatable(otherViewport.id);
|
|
1108
|
+
const viewportSlabThicknessControlsOn =
|
|
1109
|
+
this._getReferenceLineSlabThicknessControlsOn(otherViewport.id);
|
|
1110
|
+
const selectedViewportId = data.activeViewportIds.find(
|
|
1111
|
+
(id) => id === otherViewport.id
|
|
1112
|
+
);
|
|
1113
|
+
|
|
1114
|
+
let color =
|
|
1115
|
+
viewportColor !== undefined ? viewportColor : 'rgb(200, 200, 200)';
|
|
1116
|
+
|
|
1117
|
+
let lineWidth = this.widthStrokeAnnotation;
|
|
1118
|
+
|
|
1119
|
+
const lineActive =
|
|
1120
|
+
data.handles.activeOperation !== null &&
|
|
1121
|
+
data.handles.activeOperation === OPERATION.DRAG &&
|
|
1122
|
+
selectedViewportId;
|
|
1123
|
+
|
|
1124
|
+
if (lineActive) {
|
|
1125
|
+
lineWidth = 2.5;
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
let lineUID = `${lineIndex}`;
|
|
1129
|
+
if (viewportControllable && viewportDraggableRotatable) {
|
|
1130
|
+
lineUID = `${lineIndex}One`;
|
|
1131
|
+
drawLineSvg(
|
|
1132
|
+
svgDrawingHelper,
|
|
1133
|
+
annotationUID,
|
|
1134
|
+
lineUID,
|
|
1135
|
+
line[1],
|
|
1136
|
+
line[2],
|
|
1137
|
+
{
|
|
1138
|
+
color,
|
|
1139
|
+
lineWidth,
|
|
1140
|
+
}
|
|
1141
|
+
);
|
|
1142
|
+
|
|
1143
|
+
lineUID = `${lineIndex}Two`;
|
|
1144
|
+
drawLineSvg(
|
|
1145
|
+
svgDrawingHelper,
|
|
1146
|
+
annotationUID,
|
|
1147
|
+
lineUID,
|
|
1148
|
+
line[3],
|
|
1149
|
+
line[4],
|
|
1150
|
+
{
|
|
1151
|
+
color,
|
|
1152
|
+
lineWidth,
|
|
1153
|
+
}
|
|
1154
|
+
);
|
|
1155
|
+
} else {
|
|
1156
|
+
drawLineSvg(
|
|
1157
|
+
svgDrawingHelper,
|
|
1158
|
+
annotationUID,
|
|
1159
|
+
lineUID,
|
|
1160
|
+
line[2],
|
|
1161
|
+
line[4],
|
|
1162
|
+
{
|
|
1163
|
+
color,
|
|
1164
|
+
lineWidth,
|
|
1165
|
+
}
|
|
1166
|
+
);
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
if (viewportControllable) {
|
|
1170
|
+
color =
|
|
1171
|
+
viewportColor !== undefined ? viewportColor : 'rgb(200, 200, 200)';
|
|
1172
|
+
|
|
1173
|
+
const rotHandlesActive =
|
|
1174
|
+
data.handles.activeOperation === OPERATION.ROTATE;
|
|
1175
|
+
const rotationHandles = [line[9], line[10]];
|
|
1176
|
+
|
|
1177
|
+
const rotHandleWorldOne = [
|
|
1178
|
+
viewport.canvasToWorld(line[9]),
|
|
1179
|
+
otherViewport,
|
|
1180
|
+
line[1],
|
|
1181
|
+
line[2],
|
|
1182
|
+
];
|
|
1183
|
+
const rotHandleWorldTwo = [
|
|
1184
|
+
viewport.canvasToWorld(line[10]),
|
|
1185
|
+
otherViewport,
|
|
1186
|
+
line[3],
|
|
1187
|
+
line[4],
|
|
1188
|
+
];
|
|
1189
|
+
newRtpoints.push(rotHandleWorldOne, rotHandleWorldTwo);
|
|
1190
|
+
|
|
1191
|
+
const slabThicknessHandlesActive =
|
|
1192
|
+
data.handles.activeOperation === OPERATION.SLAB;
|
|
1193
|
+
const slabThicknessHandles = [line[11], line[12], line[13], line[14]];
|
|
1194
|
+
|
|
1195
|
+
const slabThicknessHandleWorldOne = [
|
|
1196
|
+
viewport.canvasToWorld(line[11]),
|
|
1197
|
+
otherViewport,
|
|
1198
|
+
line[5],
|
|
1199
|
+
line[6],
|
|
1200
|
+
];
|
|
1201
|
+
const slabThicknessHandleWorldTwo = [
|
|
1202
|
+
viewport.canvasToWorld(line[12]),
|
|
1203
|
+
otherViewport,
|
|
1204
|
+
line[5],
|
|
1205
|
+
line[6],
|
|
1206
|
+
];
|
|
1207
|
+
const slabThicknessHandleWorldThree = [
|
|
1208
|
+
viewport.canvasToWorld(line[13]),
|
|
1209
|
+
otherViewport,
|
|
1210
|
+
line[7],
|
|
1211
|
+
line[8],
|
|
1212
|
+
];
|
|
1213
|
+
const slabThicknessHandleWorldFour = [
|
|
1214
|
+
viewport.canvasToWorld(line[14]),
|
|
1215
|
+
otherViewport,
|
|
1216
|
+
line[7],
|
|
1217
|
+
line[8],
|
|
1218
|
+
];
|
|
1219
|
+
newStpoints.push(
|
|
1220
|
+
slabThicknessHandleWorldOne,
|
|
1221
|
+
slabThicknessHandleWorldTwo,
|
|
1222
|
+
slabThicknessHandleWorldThree,
|
|
1223
|
+
slabThicknessHandleWorldFour
|
|
1224
|
+
);
|
|
1225
|
+
|
|
1226
|
+
if (
|
|
1227
|
+
lineActive &&
|
|
1228
|
+
!rotHandlesActive &&
|
|
1229
|
+
!slabThicknessHandlesActive &&
|
|
1230
|
+
viewportDraggableRotatable &&
|
|
1231
|
+
viewportSlabThicknessControlsOn
|
|
1232
|
+
) {
|
|
1233
|
+
// draw all handles inactive (rotation and slab thickness)
|
|
1234
|
+
let handleUID = `${lineIndex}One`;
|
|
1235
|
+
drawHandlesSvg(
|
|
1236
|
+
svgDrawingHelper,
|
|
1237
|
+
annotationUID,
|
|
1238
|
+
handleUID,
|
|
1239
|
+
rotationHandles,
|
|
1240
|
+
{
|
|
1241
|
+
color,
|
|
1242
|
+
handleRadius: 3,
|
|
1243
|
+
type: 'circle',
|
|
1244
|
+
}
|
|
1245
|
+
);
|
|
1246
|
+
handleUID = `${lineIndex}Two`;
|
|
1247
|
+
drawHandlesSvg(
|
|
1248
|
+
svgDrawingHelper,
|
|
1249
|
+
annotationUID,
|
|
1250
|
+
handleUID,
|
|
1251
|
+
slabThicknessHandles,
|
|
1252
|
+
{
|
|
1253
|
+
color,
|
|
1254
|
+
handleRadius: 3,
|
|
1255
|
+
type: 'rect',
|
|
1256
|
+
}
|
|
1257
|
+
);
|
|
1258
|
+
} else if (
|
|
1259
|
+
lineActive &&
|
|
1260
|
+
!rotHandlesActive &&
|
|
1261
|
+
!slabThicknessHandlesActive &&
|
|
1262
|
+
viewportDraggableRotatable
|
|
1263
|
+
) {
|
|
1264
|
+
const handleUID = `${lineIndex}`;
|
|
1265
|
+
// draw rotation handles inactive
|
|
1266
|
+
drawHandlesSvg(
|
|
1267
|
+
svgDrawingHelper,
|
|
1268
|
+
annotationUID,
|
|
1269
|
+
handleUID,
|
|
1270
|
+
rotationHandles,
|
|
1271
|
+
{
|
|
1272
|
+
color,
|
|
1273
|
+
handleRadius: 3,
|
|
1274
|
+
type: 'circle',
|
|
1275
|
+
}
|
|
1276
|
+
);
|
|
1277
|
+
} else if (
|
|
1278
|
+
selectedViewportId &&
|
|
1279
|
+
!rotHandlesActive &&
|
|
1280
|
+
!slabThicknessHandlesActive &&
|
|
1281
|
+
viewportSlabThicknessControlsOn
|
|
1282
|
+
) {
|
|
1283
|
+
const handleUID = `${lineIndex}`;
|
|
1284
|
+
// draw slab thickness handles inactive
|
|
1285
|
+
drawHandlesSvg(
|
|
1286
|
+
svgDrawingHelper,
|
|
1287
|
+
annotationUID,
|
|
1288
|
+
handleUID,
|
|
1289
|
+
slabThicknessHandles,
|
|
1290
|
+
{
|
|
1291
|
+
color,
|
|
1292
|
+
handleRadius: 3,
|
|
1293
|
+
type: 'rect',
|
|
1294
|
+
}
|
|
1295
|
+
);
|
|
1296
|
+
} else if (rotHandlesActive && viewportDraggableRotatable) {
|
|
1297
|
+
const handleUID = `${lineIndex}`;
|
|
1298
|
+
// draw all rotation handles as active
|
|
1299
|
+
drawHandlesSvg(
|
|
1300
|
+
svgDrawingHelper,
|
|
1301
|
+
annotationUID,
|
|
1302
|
+
handleUID,
|
|
1303
|
+
rotationHandles,
|
|
1304
|
+
{
|
|
1305
|
+
color,
|
|
1306
|
+
handleRadius: 2,
|
|
1307
|
+
fill: color,
|
|
1308
|
+
type: 'circle',
|
|
1309
|
+
}
|
|
1310
|
+
);
|
|
1311
|
+
} else if (
|
|
1312
|
+
slabThicknessHandlesActive &&
|
|
1313
|
+
selectedViewportId &&
|
|
1314
|
+
viewportSlabThicknessControlsOn
|
|
1315
|
+
) {
|
|
1316
|
+
// draw only the slab thickness handles for the active viewport as active
|
|
1317
|
+
drawHandlesSvg(
|
|
1318
|
+
svgDrawingHelper,
|
|
1319
|
+
annotationUID,
|
|
1320
|
+
lineUID,
|
|
1321
|
+
slabThicknessHandles,
|
|
1322
|
+
{
|
|
1323
|
+
color,
|
|
1324
|
+
handleRadius: 2,
|
|
1325
|
+
fill: color,
|
|
1326
|
+
type: 'rect',
|
|
1327
|
+
}
|
|
1328
|
+
);
|
|
1329
|
+
}
|
|
1330
|
+
const slabThicknessValue = otherViewport.getSlabThickness();
|
|
1331
|
+
if (slabThicknessValue > 0.5 && viewportSlabThicknessControlsOn) {
|
|
1332
|
+
// draw slab thickness reference lines
|
|
1333
|
+
lineUID = `${lineIndex}STOne`;
|
|
1334
|
+
drawLineSvg(
|
|
1335
|
+
svgDrawingHelper,
|
|
1336
|
+
annotationUID,
|
|
1337
|
+
lineUID,
|
|
1338
|
+
line[5],
|
|
1339
|
+
line[6],
|
|
1340
|
+
{
|
|
1341
|
+
color,
|
|
1342
|
+
width: 1,
|
|
1343
|
+
lineDash: [2, 3],
|
|
1344
|
+
}
|
|
1345
|
+
);
|
|
1346
|
+
|
|
1347
|
+
lineUID = `${lineIndex}STTwo`;
|
|
1348
|
+
drawLineSvg(
|
|
1349
|
+
svgDrawingHelper,
|
|
1350
|
+
annotationUID,
|
|
1351
|
+
lineUID,
|
|
1352
|
+
line[7],
|
|
1353
|
+
line[8],
|
|
1354
|
+
{
|
|
1355
|
+
color,
|
|
1356
|
+
width: line,
|
|
1357
|
+
lineDash: [2, 3],
|
|
1358
|
+
}
|
|
1359
|
+
);
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
});
|
|
1363
|
+
|
|
1364
|
+
renderStatus = true;
|
|
1365
|
+
|
|
1366
|
+
// Save new handles points in annotation
|
|
1367
|
+
data.handles.rotationPoints = newRtpoints;
|
|
1368
|
+
data.handles.slabThicknessPoints = newStpoints;
|
|
1369
|
+
|
|
1370
|
+
if (this.configuration.viewportIndicators) {
|
|
1371
|
+
// render a circle to pin point the viewport color
|
|
1372
|
+
// TODO: This should not be part of the tool, and definitely not part of the renderAnnotation loop
|
|
1373
|
+
const referenceColorCoordinates = [
|
|
1374
|
+
clientWidth * 0.95,
|
|
1375
|
+
clientHeight * 0.05,
|
|
1376
|
+
] as Types.Point2;
|
|
1377
|
+
const circleRadius = canvasDiagonalLength * 0.01;
|
|
1378
|
+
|
|
1379
|
+
const circleUID = '0';
|
|
1380
|
+
drawCircleSvg(
|
|
1381
|
+
svgDrawingHelper,
|
|
1382
|
+
annotationUID,
|
|
1383
|
+
circleUID,
|
|
1384
|
+
referenceColorCoordinates,
|
|
1385
|
+
circleRadius,
|
|
1386
|
+
{ color, fill: color }
|
|
1387
|
+
);
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
return renderStatus;
|
|
1391
|
+
};
|
|
1392
|
+
|
|
1393
|
+
_getAnnotations = (enabledElement: Types.IEnabledElement) => {
|
|
1394
|
+
const { viewport } = enabledElement;
|
|
1395
|
+
return getAnnotations(this.getToolName(), viewport.element);
|
|
1396
|
+
};
|
|
1397
|
+
|
|
1398
|
+
_onNewVolume = (e: any) => {
|
|
1399
|
+
const viewportsInfo = this._getViewportsInfo();
|
|
1400
|
+
this.computeToolCenter(viewportsInfo);
|
|
1401
|
+
};
|
|
1402
|
+
|
|
1403
|
+
_unsubscribeToViewportNewVolumeSet(viewportsInfo) {
|
|
1404
|
+
viewportsInfo.forEach(({ viewportId, renderingEngineId }) => {
|
|
1405
|
+
const { viewport } = getEnabledElementByIds(
|
|
1406
|
+
viewportId,
|
|
1407
|
+
renderingEngineId
|
|
1408
|
+
);
|
|
1409
|
+
const { element } = viewport;
|
|
1410
|
+
|
|
1411
|
+
element.removeEventListener(
|
|
1412
|
+
Enums.Events.VOLUME_VIEWPORT_NEW_VOLUME,
|
|
1413
|
+
this._onNewVolume
|
|
1414
|
+
);
|
|
1415
|
+
});
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
_subscribeToViewportNewVolumeSet(viewports) {
|
|
1419
|
+
viewports.forEach(({ viewportId, renderingEngineId }) => {
|
|
1420
|
+
const { viewport } = getEnabledElementByIds(
|
|
1421
|
+
viewportId,
|
|
1422
|
+
renderingEngineId
|
|
1423
|
+
);
|
|
1424
|
+
const { element } = viewport;
|
|
1425
|
+
|
|
1426
|
+
element.addEventListener(
|
|
1427
|
+
Enums.Events.VOLUME_VIEWPORT_NEW_VOLUME,
|
|
1428
|
+
this._onNewVolume
|
|
1429
|
+
);
|
|
1430
|
+
});
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
_autoPanViewportIfNecessary(
|
|
1434
|
+
viewportId: string,
|
|
1435
|
+
renderingEngine: Types.IRenderingEngine
|
|
1436
|
+
): void {
|
|
1437
|
+
// 1. Compute the current world bounding box of the viewport from corner to corner
|
|
1438
|
+
// 2. Check if the toolCenter is outside of the world bounding box
|
|
1439
|
+
// 3. If it is outside, pan the viewport to fit in the toolCenter
|
|
1440
|
+
|
|
1441
|
+
const viewport = renderingEngine.getViewport(viewportId);
|
|
1442
|
+
const { clientWidth, clientHeight } = viewport.canvas;
|
|
1443
|
+
const topLefWorld = viewport.canvasToWorld([0, 0]);
|
|
1444
|
+
const bottomRightWorld = viewport.canvasToWorld([
|
|
1445
|
+
clientWidth,
|
|
1446
|
+
clientHeight,
|
|
1447
|
+
]);
|
|
1448
|
+
const topRightWorld = viewport.canvasToWorld([clientWidth, 0]);
|
|
1449
|
+
const bottomLeftWorld = viewport.canvasToWorld([0, clientHeight]);
|
|
1450
|
+
|
|
1451
|
+
// find the minimum and maximum world coordinates in each x,y,z
|
|
1452
|
+
const minX = Math.min(
|
|
1453
|
+
topLefWorld[0],
|
|
1454
|
+
bottomRightWorld[0],
|
|
1455
|
+
topRightWorld[0],
|
|
1456
|
+
bottomLeftWorld[0]
|
|
1457
|
+
);
|
|
1458
|
+
const maxX = Math.max(
|
|
1459
|
+
topLefWorld[0],
|
|
1460
|
+
bottomRightWorld[0],
|
|
1461
|
+
topRightWorld[0],
|
|
1462
|
+
bottomLeftWorld[0]
|
|
1463
|
+
);
|
|
1464
|
+
const minY = Math.min(
|
|
1465
|
+
topLefWorld[1],
|
|
1466
|
+
bottomRightWorld[1],
|
|
1467
|
+
topRightWorld[1],
|
|
1468
|
+
bottomLeftWorld[1]
|
|
1469
|
+
);
|
|
1470
|
+
const maxY = Math.max(
|
|
1471
|
+
topLefWorld[1],
|
|
1472
|
+
bottomRightWorld[1],
|
|
1473
|
+
topRightWorld[1],
|
|
1474
|
+
bottomLeftWorld[1]
|
|
1475
|
+
);
|
|
1476
|
+
const minZ = Math.min(
|
|
1477
|
+
topLefWorld[2],
|
|
1478
|
+
bottomRightWorld[2],
|
|
1479
|
+
topRightWorld[2],
|
|
1480
|
+
bottomLeftWorld[2]
|
|
1481
|
+
);
|
|
1482
|
+
const maxZ = Math.max(
|
|
1483
|
+
topLefWorld[2],
|
|
1484
|
+
bottomRightWorld[2],
|
|
1485
|
+
topRightWorld[2],
|
|
1486
|
+
bottomLeftWorld[2]
|
|
1487
|
+
);
|
|
1488
|
+
|
|
1489
|
+
// pan the viewport to fit the toolCenter in the direction
|
|
1490
|
+
// that is out of bounds
|
|
1491
|
+
let deltaPointsWorld;
|
|
1492
|
+
const pan = this.configuration.autoPan.panSize;
|
|
1493
|
+
|
|
1494
|
+
if (this.toolCenter[0] < minX - EPSILON) {
|
|
1495
|
+
deltaPointsWorld = [minX - this.toolCenter[0] + pan, 0, 0];
|
|
1496
|
+
} else if (this.toolCenter[0] > maxX + EPSILON) {
|
|
1497
|
+
deltaPointsWorld = [maxX - this.toolCenter[0] - pan, 0, 0];
|
|
1498
|
+
} else if (this.toolCenter[1] < minY - EPSILON) {
|
|
1499
|
+
deltaPointsWorld = [0, minY - this.toolCenter[1] + pan, 0];
|
|
1500
|
+
} else if (this.toolCenter[1] > maxY + EPSILON) {
|
|
1501
|
+
deltaPointsWorld = [0, maxY - this.toolCenter[1] - pan, 0];
|
|
1502
|
+
} else if (this.toolCenter[2] < minZ - EPSILON) {
|
|
1503
|
+
deltaPointsWorld = [0, 0, minZ - this.toolCenter[2] + pan];
|
|
1504
|
+
} else if (this.toolCenter[2] > maxZ + EPSILON) {
|
|
1505
|
+
deltaPointsWorld = [0, 0, maxZ - this.toolCenter[2] - pan];
|
|
1506
|
+
} else {
|
|
1507
|
+
return;
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
const camera = viewport.getCamera();
|
|
1511
|
+
const { focalPoint, position } = camera;
|
|
1512
|
+
|
|
1513
|
+
const updatedPosition = <Types.Point3>[
|
|
1514
|
+
position[0] - deltaPointsWorld[0],
|
|
1515
|
+
position[1] - deltaPointsWorld[1],
|
|
1516
|
+
position[2] - deltaPointsWorld[2],
|
|
1517
|
+
];
|
|
1518
|
+
|
|
1519
|
+
const updatedFocalPoint = <Types.Point3>[
|
|
1520
|
+
focalPoint[0] - deltaPointsWorld[0],
|
|
1521
|
+
focalPoint[1] - deltaPointsWorld[1],
|
|
1522
|
+
focalPoint[2] - deltaPointsWorld[2],
|
|
1523
|
+
];
|
|
1524
|
+
|
|
1525
|
+
viewport.setCamera({
|
|
1526
|
+
focalPoint: updatedFocalPoint,
|
|
1527
|
+
position: updatedPosition,
|
|
1528
|
+
});
|
|
1529
|
+
|
|
1530
|
+
viewport.render();
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
_areViewportIdArraysEqual = (viewportIdArrayOne, viewportIdArrayTwo) => {
|
|
1534
|
+
if (viewportIdArrayOne.length !== viewportIdArrayTwo.length) {
|
|
1535
|
+
return false;
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
viewportIdArrayOne.forEach((id) => {
|
|
1539
|
+
let itemFound = false;
|
|
1540
|
+
for (let i = 0; i < viewportIdArrayTwo.length; ++i) {
|
|
1541
|
+
if (id === viewportIdArrayTwo[i]) {
|
|
1542
|
+
itemFound = true;
|
|
1543
|
+
break;
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
if (itemFound === false) {
|
|
1547
|
+
return false;
|
|
1548
|
+
}
|
|
1549
|
+
});
|
|
1550
|
+
|
|
1551
|
+
return true;
|
|
1552
|
+
};
|
|
1553
|
+
|
|
1554
|
+
// It filters the viewports with crosshairs and only return viewports
|
|
1555
|
+
// that have different camera.
|
|
1556
|
+
_getAnnotationsForViewportsWithDifferentCameras = (
|
|
1557
|
+
enabledElement,
|
|
1558
|
+
annotations
|
|
1559
|
+
) => {
|
|
1560
|
+
const { viewportId, renderingEngine, viewport } = enabledElement;
|
|
1561
|
+
|
|
1562
|
+
const otherViewportAnnotations = annotations.filter(
|
|
1563
|
+
(annotation) => annotation.data.viewportId !== viewportId
|
|
1564
|
+
);
|
|
1565
|
+
|
|
1566
|
+
if (!otherViewportAnnotations || !otherViewportAnnotations.length) {
|
|
1567
|
+
return [];
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
const camera = viewport.getCamera();
|
|
1571
|
+
const { viewPlaneNormal, position } = camera;
|
|
1572
|
+
|
|
1573
|
+
const viewportsWithDifferentCameras = otherViewportAnnotations.filter(
|
|
1574
|
+
(annotation) => {
|
|
1575
|
+
const { viewportId } = annotation.data;
|
|
1576
|
+
const targetViewport = renderingEngine.getViewport(viewportId);
|
|
1577
|
+
const cameraOfTarget = targetViewport.getCamera();
|
|
1578
|
+
|
|
1579
|
+
return !(
|
|
1580
|
+
csUtils.isEqual(
|
|
1581
|
+
cameraOfTarget.viewPlaneNormal,
|
|
1582
|
+
viewPlaneNormal,
|
|
1583
|
+
1e-2
|
|
1584
|
+
) && csUtils.isEqual(cameraOfTarget.position, position, 1)
|
|
1585
|
+
);
|
|
1586
|
+
}
|
|
1587
|
+
);
|
|
1588
|
+
|
|
1589
|
+
return viewportsWithDifferentCameras;
|
|
1590
|
+
};
|
|
1591
|
+
|
|
1592
|
+
_filterViewportWithSameOrientation = (
|
|
1593
|
+
enabledElement,
|
|
1594
|
+
referenceAnnotation,
|
|
1595
|
+
annotations
|
|
1596
|
+
) => {
|
|
1597
|
+
const { renderingEngine } = enabledElement;
|
|
1598
|
+
const { data } = referenceAnnotation;
|
|
1599
|
+
const viewport = renderingEngine.getViewport(data.viewportId);
|
|
1600
|
+
|
|
1601
|
+
const linkedViewportAnnotations = annotations.filter((annotation) => {
|
|
1602
|
+
const { data } = annotation;
|
|
1603
|
+
const otherViewport = renderingEngine.getViewport(data.viewportId);
|
|
1604
|
+
const otherViewportControllable = this._getReferenceLineControllable(
|
|
1605
|
+
otherViewport.id
|
|
1606
|
+
);
|
|
1607
|
+
|
|
1608
|
+
return otherViewportControllable === true;
|
|
1609
|
+
});
|
|
1610
|
+
|
|
1611
|
+
if (!linkedViewportAnnotations || !linkedViewportAnnotations.length) {
|
|
1612
|
+
return [];
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
const camera = viewport.getCamera();
|
|
1616
|
+
const viewPlaneNormal = camera.viewPlaneNormal;
|
|
1617
|
+
vtkMath.normalize(viewPlaneNormal);
|
|
1618
|
+
|
|
1619
|
+
const otherViewportsAnnotationsWithSameCameraDirection =
|
|
1620
|
+
linkedViewportAnnotations.filter((annotation) => {
|
|
1621
|
+
const { viewportId } = annotation.data;
|
|
1622
|
+
const otherViewport = renderingEngine.getViewport(viewportId);
|
|
1623
|
+
const otherCamera = otherViewport.getCamera();
|
|
1624
|
+
const otherViewPlaneNormal = otherCamera.viewPlaneNormal;
|
|
1625
|
+
vtkMath.normalize(otherViewPlaneNormal);
|
|
1626
|
+
|
|
1627
|
+
return (
|
|
1628
|
+
csUtils.isEqual(viewPlaneNormal, otherViewPlaneNormal, 1e-2) &&
|
|
1629
|
+
csUtils.isEqual(camera.viewUp, otherCamera.viewUp, 1e-2)
|
|
1630
|
+
);
|
|
1631
|
+
});
|
|
1632
|
+
|
|
1633
|
+
return otherViewportsAnnotationsWithSameCameraDirection;
|
|
1634
|
+
};
|
|
1635
|
+
|
|
1636
|
+
_filterAnnotationsByUniqueViewportOrientations = (
|
|
1637
|
+
enabledElement,
|
|
1638
|
+
annotations
|
|
1639
|
+
) => {
|
|
1640
|
+
const { renderingEngine, viewport } = enabledElement;
|
|
1641
|
+
const camera = viewport.getCamera();
|
|
1642
|
+
const viewPlaneNormal = camera.viewPlaneNormal;
|
|
1643
|
+
vtkMath.normalize(viewPlaneNormal);
|
|
1644
|
+
|
|
1645
|
+
const otherLinkedViewportAnnotationsFromSameScene = annotations.filter(
|
|
1646
|
+
(annotation) => {
|
|
1647
|
+
const { data } = annotation;
|
|
1648
|
+
const otherViewport = renderingEngine.getViewport(data.viewportId);
|
|
1649
|
+
const otherViewportControllable = this._getReferenceLineControllable(
|
|
1650
|
+
otherViewport.id
|
|
1651
|
+
);
|
|
1652
|
+
|
|
1653
|
+
return (
|
|
1654
|
+
viewport !== otherViewport &&
|
|
1655
|
+
// scene === otherScene &&
|
|
1656
|
+
otherViewportControllable === true
|
|
1657
|
+
);
|
|
1658
|
+
}
|
|
1659
|
+
);
|
|
1660
|
+
|
|
1661
|
+
const otherViewportsAnnotationsWithUniqueCameras = [];
|
|
1662
|
+
// Iterate first on other viewport from the same scene linked
|
|
1663
|
+
for (
|
|
1664
|
+
let i = 0;
|
|
1665
|
+
i < otherLinkedViewportAnnotationsFromSameScene.length;
|
|
1666
|
+
++i
|
|
1667
|
+
) {
|
|
1668
|
+
const annotation = otherLinkedViewportAnnotationsFromSameScene[i];
|
|
1669
|
+
const { viewportId } = annotation.data;
|
|
1670
|
+
const otherViewport = renderingEngine.getViewport(viewportId);
|
|
1671
|
+
const otherCamera = otherViewport.getCamera();
|
|
1672
|
+
const otherViewPlaneNormal = otherCamera.viewPlaneNormal;
|
|
1673
|
+
vtkMath.normalize(otherViewPlaneNormal);
|
|
1674
|
+
|
|
1675
|
+
if (
|
|
1676
|
+
csUtils.isEqual(viewPlaneNormal, otherViewPlaneNormal, 1e-2) ||
|
|
1677
|
+
csUtils.isOpposite(viewPlaneNormal, otherViewPlaneNormal, 1e-2)
|
|
1678
|
+
) {
|
|
1679
|
+
continue;
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1682
|
+
let cameraFound = false;
|
|
1683
|
+
for (
|
|
1684
|
+
let jj = 0;
|
|
1685
|
+
jj < otherViewportsAnnotationsWithUniqueCameras.length;
|
|
1686
|
+
++jj
|
|
1687
|
+
) {
|
|
1688
|
+
const annotation = otherViewportsAnnotationsWithUniqueCameras[jj];
|
|
1689
|
+
const { viewportId } = annotation.data;
|
|
1690
|
+
const stockedViewport = renderingEngine.getViewport(viewportId);
|
|
1691
|
+
const cameraOfStocked = stockedViewport.getCamera();
|
|
1692
|
+
|
|
1693
|
+
if (
|
|
1694
|
+
csUtils.isEqual(
|
|
1695
|
+
cameraOfStocked.viewPlaneNormal,
|
|
1696
|
+
otherCamera.viewPlaneNormal,
|
|
1697
|
+
1e-2
|
|
1698
|
+
) &&
|
|
1699
|
+
csUtils.isEqual(cameraOfStocked.position, otherCamera.position, 1)
|
|
1700
|
+
) {
|
|
1701
|
+
cameraFound = true;
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1705
|
+
if (!cameraFound) {
|
|
1706
|
+
otherViewportsAnnotationsWithUniqueCameras.push(annotation);
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1710
|
+
const otherNonLinkedViewportAnnotationsFromSameScene = annotations.filter(
|
|
1711
|
+
(annotation) => {
|
|
1712
|
+
const { data } = annotation;
|
|
1713
|
+
const otherViewport = renderingEngine.getViewport(data.viewportId);
|
|
1714
|
+
const otherViewportControllable = this._getReferenceLineControllable(
|
|
1715
|
+
otherViewport.id
|
|
1716
|
+
);
|
|
1717
|
+
|
|
1718
|
+
return (
|
|
1719
|
+
viewport !== otherViewport &&
|
|
1720
|
+
// scene === otherScene &&
|
|
1721
|
+
otherViewportControllable !== true
|
|
1722
|
+
);
|
|
1723
|
+
}
|
|
1724
|
+
);
|
|
1725
|
+
|
|
1726
|
+
// Iterate second on other viewport from the same scene non linked
|
|
1727
|
+
for (
|
|
1728
|
+
let i = 0;
|
|
1729
|
+
i < otherNonLinkedViewportAnnotationsFromSameScene.length;
|
|
1730
|
+
++i
|
|
1731
|
+
) {
|
|
1732
|
+
const annotation = otherNonLinkedViewportAnnotationsFromSameScene[i];
|
|
1733
|
+
const { viewportId } = annotation.data;
|
|
1734
|
+
const otherViewport = renderingEngine.getViewport(viewportId);
|
|
1735
|
+
|
|
1736
|
+
const otherCamera = otherViewport.getCamera();
|
|
1737
|
+
const otherViewPlaneNormal = otherCamera.viewPlaneNormal;
|
|
1738
|
+
vtkMath.normalize(otherViewPlaneNormal);
|
|
1739
|
+
|
|
1740
|
+
if (
|
|
1741
|
+
csUtils.isEqual(viewPlaneNormal, otherViewPlaneNormal, 1e-2) ||
|
|
1742
|
+
csUtils.isOpposite(viewPlaneNormal, otherViewPlaneNormal, 1e-2)
|
|
1743
|
+
) {
|
|
1744
|
+
continue;
|
|
1745
|
+
}
|
|
1746
|
+
|
|
1747
|
+
let cameraFound = false;
|
|
1748
|
+
for (
|
|
1749
|
+
let jj = 0;
|
|
1750
|
+
jj < otherViewportsAnnotationsWithUniqueCameras.length;
|
|
1751
|
+
++jj
|
|
1752
|
+
) {
|
|
1753
|
+
const annotation = otherViewportsAnnotationsWithUniqueCameras[jj];
|
|
1754
|
+
const { viewportId } = annotation.data;
|
|
1755
|
+
const stockedViewport = renderingEngine.getViewport(viewportId);
|
|
1756
|
+
const cameraOfStocked = stockedViewport.getCamera();
|
|
1757
|
+
|
|
1758
|
+
if (
|
|
1759
|
+
csUtils.isEqual(
|
|
1760
|
+
cameraOfStocked.viewPlaneNormal,
|
|
1761
|
+
otherCamera.viewPlaneNormal,
|
|
1762
|
+
1e-2
|
|
1763
|
+
) &&
|
|
1764
|
+
csUtils.isEqual(cameraOfStocked.position, otherCamera.position, 1)
|
|
1765
|
+
) {
|
|
1766
|
+
cameraFound = true;
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1770
|
+
if (!cameraFound) {
|
|
1771
|
+
otherViewportsAnnotationsWithUniqueCameras.push(annotation);
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1775
|
+
// Iterate on all the viewport
|
|
1776
|
+
const otherViewportAnnotations =
|
|
1777
|
+
this._getAnnotationsForViewportsWithDifferentCameras(
|
|
1778
|
+
enabledElement,
|
|
1779
|
+
annotations
|
|
1780
|
+
);
|
|
1781
|
+
|
|
1782
|
+
for (let i = 0; i < otherViewportAnnotations.length; ++i) {
|
|
1783
|
+
const annotation = otherViewportAnnotations[i];
|
|
1784
|
+
if (
|
|
1785
|
+
otherViewportsAnnotationsWithUniqueCameras.find(
|
|
1786
|
+
(element) => element === annotation
|
|
1787
|
+
) === true
|
|
1788
|
+
) {
|
|
1789
|
+
continue;
|
|
1790
|
+
}
|
|
1791
|
+
|
|
1792
|
+
const { viewportId } = annotation.data;
|
|
1793
|
+
const otherViewport = renderingEngine.getViewport(viewportId);
|
|
1794
|
+
const otherCamera = otherViewport.getCamera();
|
|
1795
|
+
const otherViewPlaneNormal = otherCamera.viewPlaneNormal;
|
|
1796
|
+
vtkMath.normalize(otherViewPlaneNormal);
|
|
1797
|
+
|
|
1798
|
+
if (
|
|
1799
|
+
csUtils.isEqual(viewPlaneNormal, otherViewPlaneNormal, 1e-2) ||
|
|
1800
|
+
csUtils.isOpposite(viewPlaneNormal, otherViewPlaneNormal, 1e-2)
|
|
1801
|
+
) {
|
|
1802
|
+
continue;
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
let cameraFound = false;
|
|
1806
|
+
for (
|
|
1807
|
+
let jj = 0;
|
|
1808
|
+
jj < otherViewportsAnnotationsWithUniqueCameras.length;
|
|
1809
|
+
++jj
|
|
1810
|
+
) {
|
|
1811
|
+
const annotation = otherViewportsAnnotationsWithUniqueCameras[jj];
|
|
1812
|
+
const { viewportId } = annotation.data;
|
|
1813
|
+
const stockedViewport = renderingEngine.getViewport(viewportId);
|
|
1814
|
+
const cameraOfStocked = stockedViewport.getCamera();
|
|
1815
|
+
|
|
1816
|
+
if (
|
|
1817
|
+
csUtils.isEqual(
|
|
1818
|
+
cameraOfStocked.viewPlaneNormal,
|
|
1819
|
+
otherCamera.viewPlaneNormal,
|
|
1820
|
+
1e-2
|
|
1821
|
+
) &&
|
|
1822
|
+
csUtils.isEqual(cameraOfStocked.position, otherCamera.position, 1)
|
|
1823
|
+
) {
|
|
1824
|
+
cameraFound = true;
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
if (!cameraFound) {
|
|
1829
|
+
otherViewportsAnnotationsWithUniqueCameras.push(annotation);
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
return otherViewportsAnnotationsWithUniqueCameras;
|
|
1834
|
+
};
|
|
1835
|
+
|
|
1836
|
+
_checkIfViewportsRenderingSameScene = (viewport, otherViewport) => {
|
|
1837
|
+
const actors = viewport.getActors();
|
|
1838
|
+
const otherViewportActors = otherViewport.getActors();
|
|
1839
|
+
|
|
1840
|
+
let sameScene = true;
|
|
1841
|
+
|
|
1842
|
+
actors.forEach((actor) => {
|
|
1843
|
+
if (
|
|
1844
|
+
actors.length !== otherViewportActors.length ||
|
|
1845
|
+
otherViewportActors.find(({ uid }) => uid === actor.uid) === undefined
|
|
1846
|
+
) {
|
|
1847
|
+
sameScene = false;
|
|
1848
|
+
}
|
|
1849
|
+
});
|
|
1850
|
+
|
|
1851
|
+
return sameScene;
|
|
1852
|
+
};
|
|
1853
|
+
|
|
1854
|
+
_jump = (enabledElement, jumpWorld) => {
|
|
1855
|
+
state.isInteractingWithTool = true;
|
|
1856
|
+
const { viewport, renderingEngine } = enabledElement;
|
|
1857
|
+
|
|
1858
|
+
const annotations = this._getAnnotations(enabledElement);
|
|
1859
|
+
|
|
1860
|
+
const delta: Types.Point3 = [0, 0, 0];
|
|
1861
|
+
vtkMath.subtract(jumpWorld, this.toolCenter, delta);
|
|
1862
|
+
|
|
1863
|
+
// TRANSLATION
|
|
1864
|
+
// get the annotation of the other viewport which are parallel to the delta shift and are of the same scene
|
|
1865
|
+
const otherViewportAnnotations =
|
|
1866
|
+
this._getAnnotationsForViewportsWithDifferentCameras(
|
|
1867
|
+
enabledElement,
|
|
1868
|
+
annotations
|
|
1869
|
+
);
|
|
1870
|
+
|
|
1871
|
+
const viewportsAnnotationsToUpdate = otherViewportAnnotations.filter(
|
|
1872
|
+
(annotation) => {
|
|
1873
|
+
const { data } = annotation;
|
|
1874
|
+
const otherViewport = renderingEngine.getViewport(data.viewportId);
|
|
1875
|
+
|
|
1876
|
+
const sameScene = this._checkIfViewportsRenderingSameScene(
|
|
1877
|
+
viewport,
|
|
1878
|
+
otherViewport
|
|
1879
|
+
);
|
|
1880
|
+
|
|
1881
|
+
return (
|
|
1882
|
+
this._getReferenceLineControllable(otherViewport.id) &&
|
|
1883
|
+
this._getReferenceLineDraggableRotatable(otherViewport.id) &&
|
|
1884
|
+
sameScene
|
|
1885
|
+
);
|
|
1886
|
+
}
|
|
1887
|
+
);
|
|
1888
|
+
|
|
1889
|
+
if (viewportsAnnotationsToUpdate.length === 0) {
|
|
1890
|
+
state.isInteractingWithTool = false;
|
|
1891
|
+
return false;
|
|
1892
|
+
}
|
|
1893
|
+
|
|
1894
|
+
this._applyDeltaShiftToSelectedViewportCameras(
|
|
1895
|
+
renderingEngine,
|
|
1896
|
+
viewportsAnnotationsToUpdate,
|
|
1897
|
+
delta
|
|
1898
|
+
);
|
|
1899
|
+
|
|
1900
|
+
state.isInteractingWithTool = false;
|
|
1901
|
+
|
|
1902
|
+
return true;
|
|
1903
|
+
};
|
|
1904
|
+
|
|
1905
|
+
_activateModify = (element) => {
|
|
1906
|
+
state.isInteractingWithTool = true;
|
|
1907
|
+
|
|
1908
|
+
element.addEventListener(Events.MOUSE_UP, this._endCallback);
|
|
1909
|
+
element.addEventListener(Events.MOUSE_DRAG, this._dragCallback);
|
|
1910
|
+
element.addEventListener(Events.MOUSE_CLICK, this._endCallback);
|
|
1911
|
+
|
|
1912
|
+
element.addEventListener(Events.TOUCH_END, this._endCallback);
|
|
1913
|
+
element.addEventListener(Events.TOUCH_DRAG, this._dragCallback);
|
|
1914
|
+
element.addEventListener(Events.TOUCH_TAP, this._endCallback);
|
|
1915
|
+
};
|
|
1916
|
+
|
|
1917
|
+
_deactivateModify = (element) => {
|
|
1918
|
+
state.isInteractingWithTool = false;
|
|
1919
|
+
|
|
1920
|
+
element.removeEventListener(Events.MOUSE_UP, this._endCallback);
|
|
1921
|
+
element.removeEventListener(Events.MOUSE_DRAG, this._dragCallback);
|
|
1922
|
+
element.removeEventListener(Events.MOUSE_CLICK, this._endCallback);
|
|
1923
|
+
|
|
1924
|
+
element.removeEventListener(Events.TOUCH_END, this._endCallback);
|
|
1925
|
+
element.removeEventListener(Events.TOUCH_DRAG, this._dragCallback);
|
|
1926
|
+
element.removeEventListener(Events.TOUCH_TAP, this._endCallback);
|
|
1927
|
+
};
|
|
1928
|
+
|
|
1929
|
+
_endCallback = (evt: EventTypes.InteractionEventType) => {
|
|
1930
|
+
const eventDetail = evt.detail;
|
|
1931
|
+
const { element } = eventDetail;
|
|
1932
|
+
|
|
1933
|
+
this.editData.annotation.data.handles.activeOperation = null;
|
|
1934
|
+
this.editData.annotation.data.activeViewportIds = [];
|
|
1935
|
+
|
|
1936
|
+
this._deactivateModify(element);
|
|
1937
|
+
|
|
1938
|
+
resetElementCursor(element);
|
|
1939
|
+
|
|
1940
|
+
this.editData = null;
|
|
1941
|
+
|
|
1942
|
+
const enabledElement = getEnabledElement(element);
|
|
1943
|
+
const { renderingEngine } = enabledElement;
|
|
1944
|
+
|
|
1945
|
+
const requireSameOrientation = false;
|
|
1946
|
+
const viewportIdsToRender = getViewportIdsWithToolToRender(
|
|
1947
|
+
element,
|
|
1948
|
+
this.getToolName(),
|
|
1949
|
+
requireSameOrientation
|
|
1950
|
+
);
|
|
1951
|
+
|
|
1952
|
+
triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender);
|
|
1953
|
+
};
|
|
1954
|
+
|
|
1955
|
+
_dragCallback = (evt: EventTypes.InteractionEventType) => {
|
|
1956
|
+
const eventDetail = evt.detail;
|
|
1957
|
+
const delta = eventDetail.deltaPoints.world;
|
|
1958
|
+
|
|
1959
|
+
if (
|
|
1960
|
+
Math.abs(delta[0]) < 1e-3 &&
|
|
1961
|
+
Math.abs(delta[1]) < 1e-3 &&
|
|
1962
|
+
Math.abs(delta[2]) < 1e-3
|
|
1963
|
+
) {
|
|
1964
|
+
return;
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1967
|
+
const { element } = eventDetail;
|
|
1968
|
+
const enabledElement = getEnabledElement(element);
|
|
1969
|
+
const { renderingEngine, viewport } = enabledElement;
|
|
1970
|
+
const annotations = this._getAnnotations(
|
|
1971
|
+
enabledElement
|
|
1972
|
+
) as CrosshairsAnnotation[];
|
|
1973
|
+
const filteredToolAnnotations =
|
|
1974
|
+
this.filterInteractableAnnotationsForElement(element, annotations);
|
|
1975
|
+
|
|
1976
|
+
// viewport Annotation
|
|
1977
|
+
const viewportAnnotation = filteredToolAnnotations[0];
|
|
1978
|
+
if (!viewportAnnotation) {
|
|
1979
|
+
return;
|
|
1980
|
+
}
|
|
1981
|
+
|
|
1982
|
+
const { handles } = viewportAnnotation.data;
|
|
1983
|
+
const { currentPoints } = evt.detail;
|
|
1984
|
+
const canvasCoords = currentPoints.canvas;
|
|
1985
|
+
|
|
1986
|
+
if (handles.activeOperation === OPERATION.DRAG) {
|
|
1987
|
+
// TRANSLATION
|
|
1988
|
+
// get the annotation of the other viewport which are parallel to the delta shift and are of the same scene
|
|
1989
|
+
const otherViewportAnnotations =
|
|
1990
|
+
this._getAnnotationsForViewportsWithDifferentCameras(
|
|
1991
|
+
enabledElement,
|
|
1992
|
+
annotations
|
|
1993
|
+
);
|
|
1994
|
+
|
|
1995
|
+
const viewportsAnnotationsToUpdate = otherViewportAnnotations.filter(
|
|
1996
|
+
(annotation) => {
|
|
1997
|
+
const { data } = annotation;
|
|
1998
|
+
const otherViewport = renderingEngine.getViewport(data.viewportId);
|
|
1999
|
+
const otherViewportControllable = this._getReferenceLineControllable(
|
|
2000
|
+
otherViewport.id
|
|
2001
|
+
);
|
|
2002
|
+
const otherViewportDraggableRotatable =
|
|
2003
|
+
this._getReferenceLineDraggableRotatable(otherViewport.id);
|
|
2004
|
+
|
|
2005
|
+
return (
|
|
2006
|
+
otherViewportControllable === true &&
|
|
2007
|
+
otherViewportDraggableRotatable === true &&
|
|
2008
|
+
viewportAnnotation.data.activeViewportIds.find(
|
|
2009
|
+
(id) => id === otherViewport.id
|
|
2010
|
+
)
|
|
2011
|
+
);
|
|
2012
|
+
}
|
|
2013
|
+
);
|
|
2014
|
+
|
|
2015
|
+
this._applyDeltaShiftToSelectedViewportCameras(
|
|
2016
|
+
renderingEngine,
|
|
2017
|
+
viewportsAnnotationsToUpdate,
|
|
2018
|
+
delta
|
|
2019
|
+
);
|
|
2020
|
+
} else if (handles.activeOperation === OPERATION.ROTATE) {
|
|
2021
|
+
// ROTATION
|
|
2022
|
+
const otherViewportAnnotations =
|
|
2023
|
+
this._getAnnotationsForViewportsWithDifferentCameras(
|
|
2024
|
+
enabledElement,
|
|
2025
|
+
annotations
|
|
2026
|
+
);
|
|
2027
|
+
|
|
2028
|
+
const viewportsAnnotationsToUpdate = otherViewportAnnotations.filter(
|
|
2029
|
+
(annotation) => {
|
|
2030
|
+
const { data } = annotation;
|
|
2031
|
+
const otherViewport = renderingEngine.getViewport(data.viewportId);
|
|
2032
|
+
const otherViewportControllable = this._getReferenceLineControllable(
|
|
2033
|
+
otherViewport.id
|
|
2034
|
+
);
|
|
2035
|
+
const otherViewportDraggableRotatable =
|
|
2036
|
+
this._getReferenceLineDraggableRotatable(otherViewport.id);
|
|
2037
|
+
|
|
2038
|
+
return (
|
|
2039
|
+
otherViewportControllable === true &&
|
|
2040
|
+
otherViewportDraggableRotatable === true
|
|
2041
|
+
);
|
|
2042
|
+
}
|
|
2043
|
+
);
|
|
2044
|
+
|
|
2045
|
+
const dir1 = vec2.create();
|
|
2046
|
+
const dir2 = vec2.create();
|
|
2047
|
+
|
|
2048
|
+
const center: Types.Point3 = [
|
|
2049
|
+
this.toolCenter[0],
|
|
2050
|
+
this.toolCenter[1],
|
|
2051
|
+
this.toolCenter[2],
|
|
2052
|
+
];
|
|
2053
|
+
|
|
2054
|
+
const centerCanvas = viewport.worldToCanvas(center);
|
|
2055
|
+
|
|
2056
|
+
const finalPointCanvas = eventDetail.currentPoints.canvas;
|
|
2057
|
+
const originalPointCanvas = vec2.create();
|
|
2058
|
+
vec2.sub(
|
|
2059
|
+
originalPointCanvas,
|
|
2060
|
+
finalPointCanvas,
|
|
2061
|
+
eventDetail.deltaPoints.canvas
|
|
2062
|
+
);
|
|
2063
|
+
vec2.sub(dir1, originalPointCanvas, <vec2>centerCanvas);
|
|
2064
|
+
vec2.sub(dir2, finalPointCanvas, <vec2>centerCanvas);
|
|
2065
|
+
|
|
2066
|
+
let angle = vec2.angle(dir1, dir2);
|
|
2067
|
+
|
|
2068
|
+
if (
|
|
2069
|
+
this._isClockWise(centerCanvas, originalPointCanvas, finalPointCanvas)
|
|
2070
|
+
) {
|
|
2071
|
+
angle *= -1;
|
|
2072
|
+
}
|
|
2073
|
+
|
|
2074
|
+
// Rounding the angle to allow rotated handles to be undone
|
|
2075
|
+
// If we don't round and rotate handles clockwise by 0.0131233 radians,
|
|
2076
|
+
// there's no assurance that the counter-clockwise rotation occurs at
|
|
2077
|
+
// precisely -0.0131233, resulting in the drawn annotations being lost.
|
|
2078
|
+
angle = Math.round(angle * 100) / 100;
|
|
2079
|
+
|
|
2080
|
+
const rotationAxis = viewport.getCamera().viewPlaneNormal;
|
|
2081
|
+
// @ts-ignore : vtkjs incorrect typing
|
|
2082
|
+
const { matrix } = vtkMatrixBuilder
|
|
2083
|
+
.buildFromRadian()
|
|
2084
|
+
.translate(center[0], center[1], center[2])
|
|
2085
|
+
// @ts-ignore
|
|
2086
|
+
.rotate(angle, rotationAxis) //todo: why we are passing
|
|
2087
|
+
.translate(-center[0], -center[1], -center[2]);
|
|
2088
|
+
|
|
2089
|
+
const otherViewportsIds = [];
|
|
2090
|
+
// update camera for the other viewports.
|
|
2091
|
+
// NOTE: The lines then are rendered by the onCameraModified
|
|
2092
|
+
viewportsAnnotationsToUpdate.forEach((annotation) => {
|
|
2093
|
+
const { data } = annotation;
|
|
2094
|
+
data.handles.toolCenter = center;
|
|
2095
|
+
|
|
2096
|
+
const otherViewport = renderingEngine.getViewport(data.viewportId);
|
|
2097
|
+
const camera = otherViewport.getCamera();
|
|
2098
|
+
const { viewUp, position, focalPoint } = camera;
|
|
2099
|
+
|
|
2100
|
+
viewUp[0] += position[0];
|
|
2101
|
+
viewUp[1] += position[1];
|
|
2102
|
+
viewUp[2] += position[2];
|
|
2103
|
+
|
|
2104
|
+
vec3.transformMat4(focalPoint, focalPoint, matrix);
|
|
2105
|
+
vec3.transformMat4(position, position, matrix);
|
|
2106
|
+
vec3.transformMat4(viewUp, viewUp, matrix);
|
|
2107
|
+
|
|
2108
|
+
viewUp[0] -= position[0];
|
|
2109
|
+
viewUp[1] -= position[1];
|
|
2110
|
+
viewUp[2] -= position[2];
|
|
2111
|
+
|
|
2112
|
+
otherViewport.setCamera({
|
|
2113
|
+
position,
|
|
2114
|
+
viewUp,
|
|
2115
|
+
focalPoint,
|
|
2116
|
+
});
|
|
2117
|
+
otherViewportsIds.push(otherViewport.id);
|
|
2118
|
+
});
|
|
2119
|
+
renderingEngine.renderViewports(otherViewportsIds);
|
|
2120
|
+
} else if (handles.activeOperation === OPERATION.SLAB) {
|
|
2121
|
+
// SLAB THICKNESS
|
|
2122
|
+
// this should be just the active one under the mouse,
|
|
2123
|
+
const otherViewportAnnotations =
|
|
2124
|
+
this._getAnnotationsForViewportsWithDifferentCameras(
|
|
2125
|
+
enabledElement,
|
|
2126
|
+
annotations
|
|
2127
|
+
);
|
|
2128
|
+
|
|
2129
|
+
const referenceAnnotations = otherViewportAnnotations.filter(
|
|
2130
|
+
(annotation) => {
|
|
2131
|
+
const { data } = annotation;
|
|
2132
|
+
const otherViewport = renderingEngine.getViewport(data.viewportId);
|
|
2133
|
+
const otherViewportControllable = this._getReferenceLineControllable(
|
|
2134
|
+
otherViewport.id
|
|
2135
|
+
);
|
|
2136
|
+
const otherViewportSlabThicknessControlsOn =
|
|
2137
|
+
this._getReferenceLineSlabThicknessControlsOn(otherViewport.id);
|
|
2138
|
+
|
|
2139
|
+
return (
|
|
2140
|
+
otherViewportControllable === true &&
|
|
2141
|
+
otherViewportSlabThicknessControlsOn === true &&
|
|
2142
|
+
viewportAnnotation.data.activeViewportIds.find(
|
|
2143
|
+
(id) => id === otherViewport.id
|
|
2144
|
+
)
|
|
2145
|
+
);
|
|
2146
|
+
}
|
|
2147
|
+
);
|
|
2148
|
+
|
|
2149
|
+
if (referenceAnnotations.length === 0) {
|
|
2150
|
+
return;
|
|
2151
|
+
}
|
|
2152
|
+
const viewportsAnnotationsToUpdate =
|
|
2153
|
+
this._filterViewportWithSameOrientation(
|
|
2154
|
+
enabledElement,
|
|
2155
|
+
referenceAnnotations[0],
|
|
2156
|
+
annotations
|
|
2157
|
+
);
|
|
2158
|
+
|
|
2159
|
+
const viewportsIds = [];
|
|
2160
|
+
viewportsIds.push(viewport.id);
|
|
2161
|
+
viewportsAnnotationsToUpdate.forEach(
|
|
2162
|
+
(annotation: CrosshairsAnnotation) => {
|
|
2163
|
+
const { data } = annotation;
|
|
2164
|
+
|
|
2165
|
+
const otherViewport = renderingEngine.getViewport(
|
|
2166
|
+
data.viewportId
|
|
2167
|
+
) as Types.IVolumeViewport;
|
|
2168
|
+
const camera = otherViewport.getCamera();
|
|
2169
|
+
const normal = camera.viewPlaneNormal;
|
|
2170
|
+
|
|
2171
|
+
const dotProd = vtkMath.dot(delta, normal);
|
|
2172
|
+
const projectedDelta: Types.Point3 = [...normal];
|
|
2173
|
+
vtkMath.multiplyScalar(projectedDelta, dotProd);
|
|
2174
|
+
|
|
2175
|
+
if (
|
|
2176
|
+
Math.abs(projectedDelta[0]) > 1e-3 ||
|
|
2177
|
+
Math.abs(projectedDelta[1]) > 1e-3 ||
|
|
2178
|
+
Math.abs(projectedDelta[2]) > 1e-3
|
|
2179
|
+
) {
|
|
2180
|
+
const mod = Math.sqrt(
|
|
2181
|
+
projectedDelta[0] * projectedDelta[0] +
|
|
2182
|
+
projectedDelta[1] * projectedDelta[1] +
|
|
2183
|
+
projectedDelta[2] * projectedDelta[2]
|
|
2184
|
+
);
|
|
2185
|
+
|
|
2186
|
+
const currentPoint = eventDetail.lastPoints.world;
|
|
2187
|
+
const direction: Types.Point3 = [0, 0, 0];
|
|
2188
|
+
|
|
2189
|
+
const currentCenter: Types.Point3 = [
|
|
2190
|
+
this.toolCenter[0],
|
|
2191
|
+
this.toolCenter[1],
|
|
2192
|
+
this.toolCenter[2],
|
|
2193
|
+
];
|
|
2194
|
+
|
|
2195
|
+
// use this.toolCenter only if viewportDraggableRotatable
|
|
2196
|
+
const viewportDraggableRotatable =
|
|
2197
|
+
this._getReferenceLineDraggableRotatable(otherViewport.id);
|
|
2198
|
+
if (!viewportDraggableRotatable) {
|
|
2199
|
+
const { rotationPoints } = this.editData.annotation.data.handles;
|
|
2200
|
+
// Todo: what is a point uid?
|
|
2201
|
+
const otherViewportRotationPoints = rotationPoints.filter(
|
|
2202
|
+
(point) => point[1].uid === otherViewport.id
|
|
2203
|
+
);
|
|
2204
|
+
if (otherViewportRotationPoints.length === 2) {
|
|
2205
|
+
const point1 = viewport.canvasToWorld(
|
|
2206
|
+
otherViewportRotationPoints[0][3]
|
|
2207
|
+
);
|
|
2208
|
+
const point2 = viewport.canvasToWorld(
|
|
2209
|
+
otherViewportRotationPoints[1][3]
|
|
2210
|
+
);
|
|
2211
|
+
vtkMath.add(point1, point2, currentCenter);
|
|
2212
|
+
vtkMath.multiplyScalar(<Types.Point3>currentCenter, 0.5);
|
|
2213
|
+
}
|
|
2214
|
+
}
|
|
2215
|
+
|
|
2216
|
+
vtkMath.subtract(currentPoint, currentCenter, direction);
|
|
2217
|
+
const dotProdDirection = vtkMath.dot(direction, normal);
|
|
2218
|
+
const projectedDirection: Types.Point3 = [...normal];
|
|
2219
|
+
vtkMath.multiplyScalar(projectedDirection, dotProdDirection);
|
|
2220
|
+
const normalizedProjectedDirection: Types.Point3 = [
|
|
2221
|
+
projectedDirection[0],
|
|
2222
|
+
projectedDirection[1],
|
|
2223
|
+
projectedDirection[2],
|
|
2224
|
+
];
|
|
2225
|
+
vec3.normalize(
|
|
2226
|
+
normalizedProjectedDirection,
|
|
2227
|
+
normalizedProjectedDirection
|
|
2228
|
+
);
|
|
2229
|
+
const normalizedProjectedDelta: Types.Point3 = [
|
|
2230
|
+
projectedDelta[0],
|
|
2231
|
+
projectedDelta[1],
|
|
2232
|
+
projectedDelta[2],
|
|
2233
|
+
];
|
|
2234
|
+
vec3.normalize(normalizedProjectedDelta, normalizedProjectedDelta);
|
|
2235
|
+
|
|
2236
|
+
let slabThicknessValue = otherViewport.getSlabThickness();
|
|
2237
|
+
if (
|
|
2238
|
+
csUtils.isOpposite(
|
|
2239
|
+
normalizedProjectedDirection,
|
|
2240
|
+
normalizedProjectedDelta,
|
|
2241
|
+
1e-3
|
|
2242
|
+
)
|
|
2243
|
+
) {
|
|
2244
|
+
slabThicknessValue -= mod;
|
|
2245
|
+
} else {
|
|
2246
|
+
slabThicknessValue += mod;
|
|
2247
|
+
}
|
|
2248
|
+
|
|
2249
|
+
slabThicknessValue = Math.abs(slabThicknessValue);
|
|
2250
|
+
slabThicknessValue = Math.max(
|
|
2251
|
+
RENDERING_DEFAULTS.MINIMUM_SLAB_THICKNESS,
|
|
2252
|
+
slabThicknessValue
|
|
2253
|
+
);
|
|
2254
|
+
|
|
2255
|
+
const near = this._pointNearReferenceLine(
|
|
2256
|
+
viewportAnnotation,
|
|
2257
|
+
canvasCoords,
|
|
2258
|
+
6,
|
|
2259
|
+
otherViewport
|
|
2260
|
+
);
|
|
2261
|
+
|
|
2262
|
+
if (near) {
|
|
2263
|
+
slabThicknessValue = RENDERING_DEFAULTS.MINIMUM_SLAB_THICKNESS;
|
|
2264
|
+
}
|
|
2265
|
+
|
|
2266
|
+
// We want to set the slabThickness for the viewport's actors but
|
|
2267
|
+
// since the crosshairs tool instance has configuration regarding which
|
|
2268
|
+
// actorUIDs (in case of volume -> actorUID = volumeIds) to set the
|
|
2269
|
+
// slabThickness for, we need to delegate the slabThickness setting
|
|
2270
|
+
// to the crosshairs tool instance of the toolGroup since configurations
|
|
2271
|
+
// exist on the toolInstance and each toolGroup has its own crosshairs
|
|
2272
|
+
// tool instance (Otherwise, we would need to set this filterActorUIDsToSetSlabThickness at
|
|
2273
|
+
// the viewport level which makes tool and viewport state convoluted).
|
|
2274
|
+
const toolGroup = getToolGroupForViewport(
|
|
2275
|
+
otherViewport.id,
|
|
2276
|
+
renderingEngine.id
|
|
2277
|
+
);
|
|
2278
|
+
const crosshairsInstance = toolGroup.getToolInstance(
|
|
2279
|
+
this.getToolName()
|
|
2280
|
+
);
|
|
2281
|
+
crosshairsInstance.setSlabThickness(
|
|
2282
|
+
otherViewport,
|
|
2283
|
+
slabThicknessValue
|
|
2284
|
+
);
|
|
2285
|
+
|
|
2286
|
+
viewportsIds.push(otherViewport.id);
|
|
2287
|
+
}
|
|
2288
|
+
}
|
|
2289
|
+
);
|
|
2290
|
+
renderingEngine.renderViewports(viewportsIds);
|
|
2291
|
+
}
|
|
2292
|
+
};
|
|
2293
|
+
|
|
2294
|
+
setSlabThickness(viewport, slabThickness) {
|
|
2295
|
+
let actorUIDs;
|
|
2296
|
+
const { filterActorUIDsToSetSlabThickness } = this.configuration;
|
|
2297
|
+
if (
|
|
2298
|
+
filterActorUIDsToSetSlabThickness &&
|
|
2299
|
+
filterActorUIDsToSetSlabThickness.length > 0
|
|
2300
|
+
) {
|
|
2301
|
+
actorUIDs = filterActorUIDsToSetSlabThickness;
|
|
2302
|
+
}
|
|
2303
|
+
|
|
2304
|
+
let blendModeToUse = this.configuration.slabThicknessBlendMode;
|
|
2305
|
+
if (slabThickness === RENDERING_DEFAULTS.MINIMUM_SLAB_THICKNESS) {
|
|
2306
|
+
blendModeToUse = Enums.BlendModes.COMPOSITE;
|
|
2307
|
+
}
|
|
2308
|
+
|
|
2309
|
+
const immediate = false;
|
|
2310
|
+
viewport.setBlendMode(blendModeToUse, actorUIDs, immediate);
|
|
2311
|
+
viewport.setSlabThickness(slabThickness, actorUIDs);
|
|
2312
|
+
}
|
|
2313
|
+
|
|
2314
|
+
_isClockWise(a, b, c) {
|
|
2315
|
+
// return true if the rotation is clockwise
|
|
2316
|
+
return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]) > 0;
|
|
2317
|
+
}
|
|
2318
|
+
|
|
2319
|
+
_applyDeltaShiftToSelectedViewportCameras(
|
|
2320
|
+
renderingEngine,
|
|
2321
|
+
viewportsAnnotationsToUpdate,
|
|
2322
|
+
delta
|
|
2323
|
+
) {
|
|
2324
|
+
// update camera for the other viewports.
|
|
2325
|
+
// NOTE1: The lines then are rendered by the onCameraModified
|
|
2326
|
+
// NOTE2: crosshair center are automatically updated in the onCameraModified event
|
|
2327
|
+
viewportsAnnotationsToUpdate.forEach((annotation) => {
|
|
2328
|
+
this._applyDeltaShiftToViewportCamera(renderingEngine, annotation, delta);
|
|
2329
|
+
});
|
|
2330
|
+
}
|
|
2331
|
+
|
|
2332
|
+
_applyDeltaShiftToViewportCamera(
|
|
2333
|
+
renderingEngine: Types.IRenderingEngine,
|
|
2334
|
+
annotation,
|
|
2335
|
+
delta
|
|
2336
|
+
) {
|
|
2337
|
+
// update camera for the other viewports.
|
|
2338
|
+
// NOTE1: The lines then are rendered by the onCameraModified
|
|
2339
|
+
// NOTE2: crosshair center are automatically updated in the onCameraModified event
|
|
2340
|
+
const { data } = annotation;
|
|
2341
|
+
|
|
2342
|
+
const viewport = renderingEngine.getViewport(data.viewportId);
|
|
2343
|
+
const camera = viewport.getCamera();
|
|
2344
|
+
const normal = camera.viewPlaneNormal;
|
|
2345
|
+
|
|
2346
|
+
// Project delta over camera normal
|
|
2347
|
+
// (we don't need to pan, we need only to scroll the camera as in the wheel stack scroll tool)
|
|
2348
|
+
const dotProd = vtkMath.dot(delta, normal);
|
|
2349
|
+
const projectedDelta: Types.Point3 = [...normal];
|
|
2350
|
+
vtkMath.multiplyScalar(projectedDelta, dotProd);
|
|
2351
|
+
|
|
2352
|
+
if (
|
|
2353
|
+
Math.abs(projectedDelta[0]) > 1e-3 ||
|
|
2354
|
+
Math.abs(projectedDelta[1]) > 1e-3 ||
|
|
2355
|
+
Math.abs(projectedDelta[2]) > 1e-3
|
|
2356
|
+
) {
|
|
2357
|
+
const newFocalPoint: Types.Point3 = [0, 0, 0];
|
|
2358
|
+
const newPosition: Types.Point3 = [0, 0, 0];
|
|
2359
|
+
|
|
2360
|
+
vtkMath.add(camera.focalPoint, projectedDelta, newFocalPoint);
|
|
2361
|
+
vtkMath.add(camera.position, projectedDelta, newPosition);
|
|
2362
|
+
|
|
2363
|
+
viewport.setCamera({
|
|
2364
|
+
focalPoint: newFocalPoint,
|
|
2365
|
+
position: newPosition,
|
|
2366
|
+
});
|
|
2367
|
+
viewport.render();
|
|
2368
|
+
}
|
|
2369
|
+
}
|
|
2370
|
+
|
|
2371
|
+
_pointNearReferenceLine = (
|
|
2372
|
+
annotation,
|
|
2373
|
+
canvasCoords,
|
|
2374
|
+
proximity,
|
|
2375
|
+
lineViewport
|
|
2376
|
+
) => {
|
|
2377
|
+
const { data } = annotation;
|
|
2378
|
+
const { rotationPoints } = data.handles;
|
|
2379
|
+
|
|
2380
|
+
for (let i = 0; i < rotationPoints.length - 1; ++i) {
|
|
2381
|
+
const otherViewport = rotationPoints[i][1];
|
|
2382
|
+
if (otherViewport.id !== lineViewport.id) {
|
|
2383
|
+
continue;
|
|
2384
|
+
}
|
|
2385
|
+
|
|
2386
|
+
const viewportControllable = this._getReferenceLineControllable(
|
|
2387
|
+
otherViewport.id
|
|
2388
|
+
);
|
|
2389
|
+
if (!viewportControllable) {
|
|
2390
|
+
continue;
|
|
2391
|
+
}
|
|
2392
|
+
|
|
2393
|
+
const lineSegment1 = {
|
|
2394
|
+
start: {
|
|
2395
|
+
x: rotationPoints[i][2][0],
|
|
2396
|
+
y: rotationPoints[i][2][1],
|
|
2397
|
+
},
|
|
2398
|
+
end: {
|
|
2399
|
+
x: rotationPoints[i][3][0],
|
|
2400
|
+
y: rotationPoints[i][3][1],
|
|
2401
|
+
},
|
|
2402
|
+
};
|
|
2403
|
+
|
|
2404
|
+
const distanceToPoint1 = lineSegment.distanceToPoint(
|
|
2405
|
+
[lineSegment1.start.x, lineSegment1.start.y],
|
|
2406
|
+
[lineSegment1.end.x, lineSegment1.end.y],
|
|
2407
|
+
[canvasCoords[0], canvasCoords[1]]
|
|
2408
|
+
);
|
|
2409
|
+
|
|
2410
|
+
const lineSegment2 = {
|
|
2411
|
+
start: {
|
|
2412
|
+
x: rotationPoints[i + 1][2][0],
|
|
2413
|
+
y: rotationPoints[i + 1][2][1],
|
|
2414
|
+
},
|
|
2415
|
+
end: {
|
|
2416
|
+
x: rotationPoints[i + 1][3][0],
|
|
2417
|
+
y: rotationPoints[i + 1][3][1],
|
|
2418
|
+
},
|
|
2419
|
+
};
|
|
2420
|
+
|
|
2421
|
+
const distanceToPoint2 = lineSegment.distanceToPoint(
|
|
2422
|
+
[lineSegment2.start.x, lineSegment2.start.y],
|
|
2423
|
+
[lineSegment2.end.x, lineSegment2.end.y],
|
|
2424
|
+
[canvasCoords[0], canvasCoords[1]]
|
|
2425
|
+
);
|
|
2426
|
+
|
|
2427
|
+
if (distanceToPoint1 <= proximity || distanceToPoint2 <= proximity) {
|
|
2428
|
+
return true;
|
|
2429
|
+
}
|
|
2430
|
+
|
|
2431
|
+
// rotation handles are two for viewport
|
|
2432
|
+
i++;
|
|
2433
|
+
}
|
|
2434
|
+
|
|
2435
|
+
return false;
|
|
2436
|
+
};
|
|
2437
|
+
|
|
2438
|
+
_getRotationHandleNearImagePoint(
|
|
2439
|
+
viewport,
|
|
2440
|
+
annotation,
|
|
2441
|
+
canvasCoords,
|
|
2442
|
+
proximity
|
|
2443
|
+
) {
|
|
2444
|
+
const { data } = annotation;
|
|
2445
|
+
const { rotationPoints } = data.handles;
|
|
2446
|
+
|
|
2447
|
+
for (let i = 0; i < rotationPoints.length; i++) {
|
|
2448
|
+
const point = rotationPoints[i][0];
|
|
2449
|
+
const otherViewport = rotationPoints[i][1];
|
|
2450
|
+
const viewportControllable = this._getReferenceLineControllable(
|
|
2451
|
+
otherViewport.id
|
|
2452
|
+
);
|
|
2453
|
+
if (!viewportControllable) {
|
|
2454
|
+
continue;
|
|
2455
|
+
}
|
|
2456
|
+
|
|
2457
|
+
const viewportDraggableRotatable =
|
|
2458
|
+
this._getReferenceLineDraggableRotatable(otherViewport.id);
|
|
2459
|
+
if (!viewportDraggableRotatable) {
|
|
2460
|
+
continue;
|
|
2461
|
+
}
|
|
2462
|
+
|
|
2463
|
+
const annotationCanvasCoordinate = viewport.worldToCanvas(point);
|
|
2464
|
+
if (vec2.distance(canvasCoords, annotationCanvasCoordinate) < proximity) {
|
|
2465
|
+
data.handles.activeOperation = OPERATION.ROTATE;
|
|
2466
|
+
|
|
2467
|
+
this.editData = {
|
|
2468
|
+
annotation,
|
|
2469
|
+
};
|
|
2470
|
+
|
|
2471
|
+
return point;
|
|
2472
|
+
}
|
|
2473
|
+
}
|
|
2474
|
+
|
|
2475
|
+
return null;
|
|
2476
|
+
}
|
|
2477
|
+
|
|
2478
|
+
_getSlabThicknessHandleNearImagePoint(
|
|
2479
|
+
viewport,
|
|
2480
|
+
annotation,
|
|
2481
|
+
canvasCoords,
|
|
2482
|
+
proximity
|
|
2483
|
+
) {
|
|
2484
|
+
const { data } = annotation;
|
|
2485
|
+
const { slabThicknessPoints } = data.handles;
|
|
2486
|
+
|
|
2487
|
+
for (let i = 0; i < slabThicknessPoints.length; i++) {
|
|
2488
|
+
const point = slabThicknessPoints[i][0];
|
|
2489
|
+
const otherViewport = slabThicknessPoints[i][1];
|
|
2490
|
+
const viewportControllable = this._getReferenceLineControllable(
|
|
2491
|
+
otherViewport.id
|
|
2492
|
+
);
|
|
2493
|
+
if (!viewportControllable) {
|
|
2494
|
+
continue;
|
|
2495
|
+
}
|
|
2496
|
+
|
|
2497
|
+
const viewportSlabThicknessControlsOn =
|
|
2498
|
+
this._getReferenceLineSlabThicknessControlsOn(otherViewport.id);
|
|
2499
|
+
if (!viewportSlabThicknessControlsOn) {
|
|
2500
|
+
continue;
|
|
2501
|
+
}
|
|
2502
|
+
|
|
2503
|
+
const annotationCanvasCoordinate = viewport.worldToCanvas(point);
|
|
2504
|
+
if (vec2.distance(canvasCoords, annotationCanvasCoordinate) < proximity) {
|
|
2505
|
+
data.handles.activeOperation = OPERATION.SLAB;
|
|
2506
|
+
|
|
2507
|
+
data.activeViewportIds = [otherViewport.id];
|
|
2508
|
+
|
|
2509
|
+
this.editData = {
|
|
2510
|
+
annotation,
|
|
2511
|
+
};
|
|
2512
|
+
|
|
2513
|
+
return point;
|
|
2514
|
+
}
|
|
2515
|
+
}
|
|
2516
|
+
|
|
2517
|
+
return null;
|
|
2518
|
+
}
|
|
2519
|
+
|
|
2520
|
+
_pointNearTool(element, annotation, canvasCoords, proximity) {
|
|
2521
|
+
const enabledElement = getEnabledElement(element);
|
|
2522
|
+
const { viewport } = enabledElement;
|
|
2523
|
+
const { clientWidth, clientHeight } = viewport.canvas;
|
|
2524
|
+
const canvasDiagonalLength = Math.sqrt(
|
|
2525
|
+
clientWidth * clientWidth + clientHeight * clientHeight
|
|
2526
|
+
);
|
|
2527
|
+
const { data } = annotation;
|
|
2528
|
+
|
|
2529
|
+
const { rotationPoints } = data.handles;
|
|
2530
|
+
const { slabThicknessPoints } = data.handles;
|
|
2531
|
+
const viewportIdArray = [];
|
|
2532
|
+
|
|
2533
|
+
for (let i = 0; i < rotationPoints.length - 1; ++i) {
|
|
2534
|
+
const otherViewport = rotationPoints[i][1];
|
|
2535
|
+
const viewportControllable = this._getReferenceLineControllable(
|
|
2536
|
+
otherViewport.id
|
|
2537
|
+
);
|
|
2538
|
+
const viewportDraggableRotatable =
|
|
2539
|
+
this._getReferenceLineDraggableRotatable(otherViewport.id);
|
|
2540
|
+
|
|
2541
|
+
if (!viewportControllable || !viewportDraggableRotatable) {
|
|
2542
|
+
continue;
|
|
2543
|
+
}
|
|
2544
|
+
|
|
2545
|
+
const lineSegment1 = {
|
|
2546
|
+
start: {
|
|
2547
|
+
x: rotationPoints[i][2][0],
|
|
2548
|
+
y: rotationPoints[i][2][1],
|
|
2549
|
+
},
|
|
2550
|
+
end: {
|
|
2551
|
+
x: rotationPoints[i][3][0],
|
|
2552
|
+
y: rotationPoints[i][3][1],
|
|
2553
|
+
},
|
|
2554
|
+
};
|
|
2555
|
+
|
|
2556
|
+
const distanceToPoint1 = lineSegment.distanceToPoint(
|
|
2557
|
+
[lineSegment1.start.x, lineSegment1.start.y],
|
|
2558
|
+
[lineSegment1.end.x, lineSegment1.end.y],
|
|
2559
|
+
[canvasCoords[0], canvasCoords[1]]
|
|
2560
|
+
);
|
|
2561
|
+
|
|
2562
|
+
const lineSegment2 = {
|
|
2563
|
+
start: {
|
|
2564
|
+
x: rotationPoints[i + 1][2][0],
|
|
2565
|
+
y: rotationPoints[i + 1][2][1],
|
|
2566
|
+
},
|
|
2567
|
+
end: {
|
|
2568
|
+
x: rotationPoints[i + 1][3][0],
|
|
2569
|
+
y: rotationPoints[i + 1][3][1],
|
|
2570
|
+
},
|
|
2571
|
+
};
|
|
2572
|
+
|
|
2573
|
+
const distanceToPoint2 = lineSegment.distanceToPoint(
|
|
2574
|
+
[lineSegment2.start.x, lineSegment2.start.y],
|
|
2575
|
+
[lineSegment2.end.x, lineSegment2.end.y],
|
|
2576
|
+
[canvasCoords[0], canvasCoords[1]]
|
|
2577
|
+
);
|
|
2578
|
+
|
|
2579
|
+
if (distanceToPoint1 <= proximity || distanceToPoint2 <= proximity) {
|
|
2580
|
+
viewportIdArray.push(otherViewport.id);
|
|
2581
|
+
data.handles.activeOperation = OPERATION.DRAG;
|
|
2582
|
+
}
|
|
2583
|
+
|
|
2584
|
+
// rotation handles are two for viewport
|
|
2585
|
+
i++;
|
|
2586
|
+
}
|
|
2587
|
+
|
|
2588
|
+
for (let i = 0; i < slabThicknessPoints.length - 1; ++i) {
|
|
2589
|
+
const otherViewport = slabThicknessPoints[i][1];
|
|
2590
|
+
if (viewportIdArray.find((id) => id === otherViewport.id)) {
|
|
2591
|
+
continue;
|
|
2592
|
+
}
|
|
2593
|
+
|
|
2594
|
+
const viewportControllable = this._getReferenceLineControllable(
|
|
2595
|
+
otherViewport.id
|
|
2596
|
+
);
|
|
2597
|
+
const viewportSlabThicknessControlsOn =
|
|
2598
|
+
this._getReferenceLineSlabThicknessControlsOn(otherViewport.id);
|
|
2599
|
+
|
|
2600
|
+
if (!viewportControllable || !viewportSlabThicknessControlsOn) {
|
|
2601
|
+
continue;
|
|
2602
|
+
}
|
|
2603
|
+
|
|
2604
|
+
const stPointLineCanvas1 = slabThicknessPoints[i][2];
|
|
2605
|
+
const stPointLineCanvas2 = slabThicknessPoints[i][3];
|
|
2606
|
+
|
|
2607
|
+
const centerCanvas = vec2.create();
|
|
2608
|
+
vec2.add(centerCanvas, stPointLineCanvas1, stPointLineCanvas2);
|
|
2609
|
+
vec2.scale(centerCanvas, centerCanvas, 0.5);
|
|
2610
|
+
|
|
2611
|
+
const canvasUnitVectorFromCenter = vec2.create();
|
|
2612
|
+
vec2.subtract(
|
|
2613
|
+
canvasUnitVectorFromCenter,
|
|
2614
|
+
stPointLineCanvas1,
|
|
2615
|
+
centerCanvas
|
|
2616
|
+
);
|
|
2617
|
+
vec2.normalize(canvasUnitVectorFromCenter, canvasUnitVectorFromCenter);
|
|
2618
|
+
|
|
2619
|
+
const canvasVectorFromCenterStart = vec2.create();
|
|
2620
|
+
vec2.scale(
|
|
2621
|
+
canvasVectorFromCenterStart,
|
|
2622
|
+
canvasUnitVectorFromCenter,
|
|
2623
|
+
canvasDiagonalLength * 0.05
|
|
2624
|
+
);
|
|
2625
|
+
|
|
2626
|
+
const stPointLineCanvas1Start = vec2.create();
|
|
2627
|
+
const stPointLineCanvas2Start = vec2.create();
|
|
2628
|
+
vec2.add(
|
|
2629
|
+
stPointLineCanvas1Start,
|
|
2630
|
+
centerCanvas,
|
|
2631
|
+
canvasVectorFromCenterStart
|
|
2632
|
+
);
|
|
2633
|
+
vec2.subtract(
|
|
2634
|
+
stPointLineCanvas2Start,
|
|
2635
|
+
centerCanvas,
|
|
2636
|
+
canvasVectorFromCenterStart
|
|
2637
|
+
);
|
|
2638
|
+
|
|
2639
|
+
const lineSegment1 = {
|
|
2640
|
+
start: {
|
|
2641
|
+
x: stPointLineCanvas1Start[0],
|
|
2642
|
+
y: stPointLineCanvas1Start[1],
|
|
2643
|
+
},
|
|
2644
|
+
end: {
|
|
2645
|
+
x: stPointLineCanvas1[0],
|
|
2646
|
+
y: stPointLineCanvas1[1],
|
|
2647
|
+
},
|
|
2648
|
+
};
|
|
2649
|
+
|
|
2650
|
+
const distanceToPoint1 = lineSegment.distanceToPoint(
|
|
2651
|
+
[lineSegment1.start.x, lineSegment1.start.y],
|
|
2652
|
+
[lineSegment1.end.x, lineSegment1.end.y],
|
|
2653
|
+
[canvasCoords[0], canvasCoords[1]]
|
|
2654
|
+
);
|
|
2655
|
+
|
|
2656
|
+
const lineSegment2 = {
|
|
2657
|
+
start: {
|
|
2658
|
+
x: stPointLineCanvas2Start[0],
|
|
2659
|
+
y: stPointLineCanvas2Start[1],
|
|
2660
|
+
},
|
|
2661
|
+
end: {
|
|
2662
|
+
x: stPointLineCanvas2[0],
|
|
2663
|
+
y: stPointLineCanvas2[1],
|
|
2664
|
+
},
|
|
2665
|
+
};
|
|
2666
|
+
|
|
2667
|
+
const distanceToPoint2 = lineSegment.distanceToPoint(
|
|
2668
|
+
[lineSegment2.start.x, lineSegment2.start.y],
|
|
2669
|
+
[lineSegment2.end.x, lineSegment2.end.y],
|
|
2670
|
+
[canvasCoords[0], canvasCoords[1]]
|
|
2671
|
+
);
|
|
2672
|
+
|
|
2673
|
+
if (distanceToPoint1 <= proximity || distanceToPoint2 <= proximity) {
|
|
2674
|
+
viewportIdArray.push(otherViewport.id); // we still need this to draw inactive slab thickness handles
|
|
2675
|
+
data.handles.activeOperation = null; // no operation
|
|
2676
|
+
}
|
|
2677
|
+
|
|
2678
|
+
// slab thickness handles are in couples
|
|
2679
|
+
i++;
|
|
2680
|
+
}
|
|
2681
|
+
|
|
2682
|
+
data.activeViewportIds = [...viewportIdArray];
|
|
2683
|
+
|
|
2684
|
+
this.editData = {
|
|
2685
|
+
annotation,
|
|
2686
|
+
};
|
|
2687
|
+
|
|
2688
|
+
return data.handles.activeOperation === OPERATION.DRAG ? true : false;
|
|
2689
|
+
}
|
|
2690
|
+
}
|
|
2691
|
+
|
|
2692
|
+
CrosshairsTool.toolName = 'Crosshairs';
|
|
2693
|
+
export default CrosshairsTool;
|