@cornerstonejs/tools 1.40.3 → 1.42.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. package/dist/cjs/drawingSvg/drawHandle.d.ts +4 -0
  2. package/dist/cjs/drawingSvg/drawHandle.js +66 -0
  3. package/dist/cjs/drawingSvg/drawHandle.js.map +1 -0
  4. package/dist/cjs/drawingSvg/drawHandles.js +4 -60
  5. package/dist/cjs/drawingSvg/drawHandles.js.map +1 -1
  6. package/dist/cjs/drawingSvg/index.d.ts +2 -1
  7. package/dist/cjs/drawingSvg/index.js +3 -1
  8. package/dist/cjs/drawingSvg/index.js.map +1 -1
  9. package/dist/cjs/eventDispatchers/keyboardEventHandlers/keyDown.js +2 -1
  10. package/dist/cjs/eventDispatchers/keyboardEventHandlers/keyDown.js.map +1 -1
  11. package/dist/cjs/index.d.ts +2 -2
  12. package/dist/cjs/index.js +3 -2
  13. package/dist/cjs/index.js.map +1 -1
  14. package/dist/cjs/stateManagement/annotation/annotationState.js +3 -0
  15. package/dist/cjs/stateManagement/annotation/annotationState.js.map +1 -1
  16. package/dist/cjs/tools/annotation/AngleTool.js.map +1 -1
  17. package/dist/cjs/tools/annotation/BidirectionalTool.js +7 -6
  18. package/dist/cjs/tools/annotation/BidirectionalTool.js.map +1 -1
  19. package/dist/cjs/tools/annotation/LengthTool.js +4 -3
  20. package/dist/cjs/tools/annotation/LengthTool.js.map +1 -1
  21. package/dist/cjs/tools/annotation/ProbeTool.js +24 -3
  22. package/dist/cjs/tools/annotation/ProbeTool.js.map +1 -1
  23. package/dist/cjs/tools/annotation/UltrasoundDirectionalTool.d.ts +36 -0
  24. package/dist/cjs/tools/annotation/UltrasoundDirectionalTool.js +483 -0
  25. package/dist/cjs/tools/annotation/UltrasoundDirectionalTool.js.map +1 -0
  26. package/dist/cjs/tools/index.d.ts +2 -1
  27. package/dist/cjs/tools/index.js +3 -1
  28. package/dist/cjs/tools/index.js.map +1 -1
  29. package/dist/cjs/types/ToolSpecificAnnotationTypes.d.ts +28 -0
  30. package/dist/cjs/types/index.d.ts +2 -1
  31. package/dist/cjs/utilities/contours/AnnotationToPointData.d.ts +11 -0
  32. package/dist/cjs/utilities/contours/AnnotationToPointData.js +44 -0
  33. package/dist/cjs/utilities/contours/AnnotationToPointData.js.map +1 -0
  34. package/dist/cjs/utilities/contours/RectangleROIStartEndThreshold.d.ts +6 -0
  35. package/dist/cjs/utilities/contours/RectangleROIStartEndThreshold.js +43 -0
  36. package/dist/cjs/utilities/contours/RectangleROIStartEndThreshold.js.map +1 -0
  37. package/dist/cjs/utilities/contours/contourFinder.d.ts +7 -0
  38. package/dist/cjs/utilities/contours/contourFinder.js +68 -0
  39. package/dist/cjs/utilities/contours/contourFinder.js.map +1 -0
  40. package/dist/cjs/utilities/contours/detectContourHoles.d.ts +5 -0
  41. package/dist/cjs/utilities/contours/detectContourHoles.js +78 -0
  42. package/dist/cjs/utilities/contours/detectContourHoles.js.map +1 -0
  43. package/dist/cjs/utilities/contours/generateContourSetsFromLabelmap.d.ts +4 -0
  44. package/dist/cjs/utilities/contours/generateContourSetsFromLabelmap.js +124 -0
  45. package/dist/cjs/utilities/contours/generateContourSetsFromLabelmap.js.map +1 -0
  46. package/dist/cjs/utilities/contours/index.d.ts +6 -0
  47. package/dist/cjs/utilities/contours/index.js +17 -0
  48. package/dist/cjs/utilities/contours/index.js.map +1 -0
  49. package/dist/cjs/utilities/contours/mergePoints.d.ts +8 -0
  50. package/dist/cjs/utilities/contours/mergePoints.js +77 -0
  51. package/dist/cjs/utilities/contours/mergePoints.js.map +1 -0
  52. package/dist/cjs/utilities/getCalibratedUnits.d.ts +16 -2
  53. package/dist/cjs/utilities/getCalibratedUnits.js +127 -5
  54. package/dist/cjs/utilities/getCalibratedUnits.js.map +1 -1
  55. package/dist/cjs/utilities/index.d.ts +2 -1
  56. package/dist/cjs/utilities/index.js +3 -1
  57. package/dist/cjs/utilities/index.js.map +1 -1
  58. package/dist/cjs/utilities/segmentation/contourAndFindLargestBidirectional.d.ts +1 -0
  59. package/dist/cjs/utilities/segmentation/contourAndFindLargestBidirectional.js +31 -0
  60. package/dist/cjs/utilities/segmentation/contourAndFindLargestBidirectional.js.map +1 -0
  61. package/dist/cjs/utilities/segmentation/createBidirectionalToolData.d.ts +14 -0
  62. package/dist/cjs/utilities/segmentation/createBidirectionalToolData.js +43 -0
  63. package/dist/cjs/utilities/segmentation/createBidirectionalToolData.js.map +1 -0
  64. package/dist/cjs/utilities/segmentation/findLargestBidirectional.d.ts +1 -0
  65. package/dist/cjs/utilities/segmentation/findLargestBidirectional.js +94 -0
  66. package/dist/cjs/utilities/segmentation/findLargestBidirectional.js.map +1 -0
  67. package/dist/cjs/utilities/segmentation/index.d.ts +4 -1
  68. package/dist/cjs/utilities/segmentation/index.js +7 -1
  69. package/dist/cjs/utilities/segmentation/index.js.map +1 -1
  70. package/dist/cjs/utilities/segmentation/isLineInSegment.d.ts +9 -0
  71. package/dist/cjs/utilities/segmentation/isLineInSegment.js +55 -0
  72. package/dist/cjs/utilities/segmentation/isLineInSegment.js.map +1 -0
  73. package/dist/cjs/utilities/segmentation/segmentContourAction.d.ts +17 -0
  74. package/dist/cjs/utilities/segmentation/segmentContourAction.js +122 -0
  75. package/dist/cjs/utilities/segmentation/segmentContourAction.js.map +1 -0
  76. package/dist/esm/drawingSvg/drawHandle.js +61 -0
  77. package/dist/esm/drawingSvg/drawHandle.js.map +1 -0
  78. package/dist/esm/drawingSvg/drawHandles.js +4 -60
  79. package/dist/esm/drawingSvg/drawHandles.js.map +1 -1
  80. package/dist/esm/drawingSvg/index.js +2 -1
  81. package/dist/esm/drawingSvg/index.js.map +1 -1
  82. package/dist/esm/eventDispatchers/keyboardEventHandlers/keyDown.js +2 -1
  83. package/dist/esm/eventDispatchers/keyboardEventHandlers/keyDown.js.map +1 -1
  84. package/dist/esm/index.js +2 -2
  85. package/dist/esm/index.js.map +1 -1
  86. package/dist/esm/stateManagement/annotation/annotationState.js +3 -0
  87. package/dist/esm/stateManagement/annotation/annotationState.js.map +1 -1
  88. package/dist/esm/tools/annotation/AngleTool.js.map +1 -1
  89. package/dist/esm/tools/annotation/BidirectionalTool.js +7 -6
  90. package/dist/esm/tools/annotation/BidirectionalTool.js.map +1 -1
  91. package/dist/esm/tools/annotation/LengthTool.js +5 -4
  92. package/dist/esm/tools/annotation/LengthTool.js.map +1 -1
  93. package/dist/esm/tools/annotation/ProbeTool.js +24 -3
  94. package/dist/esm/tools/annotation/ProbeTool.js.map +1 -1
  95. package/dist/esm/tools/annotation/UltrasoundDirectionalTool.js +478 -0
  96. package/dist/esm/tools/annotation/UltrasoundDirectionalTool.js.map +1 -0
  97. package/dist/esm/tools/index.js +2 -1
  98. package/dist/esm/tools/index.js.map +1 -1
  99. package/dist/esm/utilities/contours/AnnotationToPointData.js +39 -0
  100. package/dist/esm/utilities/contours/AnnotationToPointData.js.map +1 -0
  101. package/dist/esm/utilities/contours/RectangleROIStartEndThreshold.js +41 -0
  102. package/dist/esm/utilities/contours/RectangleROIStartEndThreshold.js.map +1 -0
  103. package/dist/esm/utilities/contours/contourFinder.js +63 -0
  104. package/dist/esm/utilities/contours/contourFinder.js.map +1 -0
  105. package/dist/esm/utilities/contours/detectContourHoles.js +74 -0
  106. package/dist/esm/utilities/contours/detectContourHoles.js.map +1 -0
  107. package/dist/esm/utilities/contours/generateContourSetsFromLabelmap.js +117 -0
  108. package/dist/esm/utilities/contours/generateContourSetsFromLabelmap.js.map +1 -0
  109. package/dist/esm/utilities/contours/index.js +7 -0
  110. package/dist/esm/utilities/contours/index.js.map +1 -0
  111. package/dist/esm/utilities/contours/mergePoints.js +73 -0
  112. package/dist/esm/utilities/contours/mergePoints.js.map +1 -0
  113. package/dist/esm/utilities/getCalibratedUnits.js +125 -6
  114. package/dist/esm/utilities/getCalibratedUnits.js.map +1 -1
  115. package/dist/esm/utilities/index.js +2 -1
  116. package/dist/esm/utilities/index.js.map +1 -1
  117. package/dist/esm/utilities/segmentation/contourAndFindLargestBidirectional.js +25 -0
  118. package/dist/esm/utilities/segmentation/contourAndFindLargestBidirectional.js.map +1 -0
  119. package/dist/esm/utilities/segmentation/createBidirectionalToolData.js +40 -0
  120. package/dist/esm/utilities/segmentation/createBidirectionalToolData.js.map +1 -0
  121. package/dist/esm/utilities/segmentation/findLargestBidirectional.js +96 -0
  122. package/dist/esm/utilities/segmentation/findLargestBidirectional.js.map +1 -0
  123. package/dist/esm/utilities/segmentation/index.js +4 -1
  124. package/dist/esm/utilities/segmentation/index.js.map +1 -1
  125. package/dist/esm/utilities/segmentation/isLineInSegment.js +50 -0
  126. package/dist/esm/utilities/segmentation/isLineInSegment.js.map +1 -0
  127. package/dist/esm/utilities/segmentation/segmentContourAction.js +98 -0
  128. package/dist/esm/utilities/segmentation/segmentContourAction.js.map +1 -0
  129. package/dist/types/drawingSvg/drawHandle.d.ts +5 -0
  130. package/dist/types/drawingSvg/drawHandle.d.ts.map +1 -0
  131. package/dist/types/drawingSvg/drawHandles.d.ts.map +1 -1
  132. package/dist/types/drawingSvg/index.d.ts +2 -1
  133. package/dist/types/drawingSvg/index.d.ts.map +1 -1
  134. package/dist/types/eventDispatchers/keyboardEventHandlers/keyDown.d.ts.map +1 -1
  135. package/dist/types/index.d.ts +2 -2
  136. package/dist/types/index.d.ts.map +1 -1
  137. package/dist/types/stateManagement/annotation/annotationState.d.ts.map +1 -1
  138. package/dist/types/tools/annotation/AngleTool.d.ts.map +1 -1
  139. package/dist/types/tools/annotation/BidirectionalTool.d.ts.map +1 -1
  140. package/dist/types/tools/annotation/LengthTool.d.ts.map +1 -1
  141. package/dist/types/tools/annotation/ProbeTool.d.ts.map +1 -1
  142. package/dist/types/tools/annotation/UltrasoundDirectionalTool.d.ts +37 -0
  143. package/dist/types/tools/annotation/UltrasoundDirectionalTool.d.ts.map +1 -0
  144. package/dist/types/tools/index.d.ts +2 -1
  145. package/dist/types/tools/index.d.ts.map +1 -1
  146. package/dist/types/types/ToolSpecificAnnotationTypes.d.ts +28 -0
  147. package/dist/types/types/ToolSpecificAnnotationTypes.d.ts.map +1 -1
  148. package/dist/types/types/index.d.ts +2 -1
  149. package/dist/types/types/index.d.ts.map +1 -1
  150. package/dist/types/utilities/contours/AnnotationToPointData.d.ts +12 -0
  151. package/dist/types/utilities/contours/AnnotationToPointData.d.ts.map +1 -0
  152. package/dist/types/utilities/contours/RectangleROIStartEndThreshold.d.ts +7 -0
  153. package/dist/types/utilities/contours/RectangleROIStartEndThreshold.d.ts.map +1 -0
  154. package/dist/types/utilities/contours/contourFinder.d.ts +8 -0
  155. package/dist/types/utilities/contours/contourFinder.d.ts.map +1 -0
  156. package/dist/types/utilities/contours/detectContourHoles.d.ts +6 -0
  157. package/dist/types/utilities/contours/detectContourHoles.d.ts.map +1 -0
  158. package/dist/types/utilities/contours/generateContourSetsFromLabelmap.d.ts +5 -0
  159. package/dist/types/utilities/contours/generateContourSetsFromLabelmap.d.ts.map +1 -0
  160. package/dist/types/utilities/contours/index.d.ts +7 -0
  161. package/dist/types/utilities/contours/index.d.ts.map +1 -0
  162. package/dist/types/utilities/contours/mergePoints.d.ts +9 -0
  163. package/dist/types/utilities/contours/mergePoints.d.ts.map +1 -0
  164. package/dist/types/utilities/getCalibratedUnits.d.ts +16 -2
  165. package/dist/types/utilities/getCalibratedUnits.d.ts.map +1 -1
  166. package/dist/types/utilities/index.d.ts +2 -1
  167. package/dist/types/utilities/index.d.ts.map +1 -1
  168. package/dist/types/utilities/segmentation/contourAndFindLargestBidirectional.d.ts +2 -0
  169. package/dist/types/utilities/segmentation/contourAndFindLargestBidirectional.d.ts.map +1 -0
  170. package/dist/types/utilities/segmentation/createBidirectionalToolData.d.ts +15 -0
  171. package/dist/types/utilities/segmentation/createBidirectionalToolData.d.ts.map +1 -0
  172. package/dist/types/utilities/segmentation/findLargestBidirectional.d.ts +2 -0
  173. package/dist/types/utilities/segmentation/findLargestBidirectional.d.ts.map +1 -0
  174. package/dist/types/utilities/segmentation/index.d.ts +4 -1
  175. package/dist/types/utilities/segmentation/index.d.ts.map +1 -1
  176. package/dist/types/utilities/segmentation/isLineInSegment.d.ts +10 -0
  177. package/dist/types/utilities/segmentation/isLineInSegment.d.ts.map +1 -0
  178. package/dist/types/utilities/segmentation/segmentContourAction.d.ts +18 -0
  179. package/dist/types/utilities/segmentation/segmentContourAction.d.ts.map +1 -0
  180. package/dist/umd/index.js +1 -1
  181. package/dist/umd/index.js.map +1 -1
  182. package/package.json +3 -3
  183. package/src/drawingSvg/drawHandle.ts +88 -0
  184. package/src/drawingSvg/drawHandles.ts +9 -75
  185. package/src/drawingSvg/index.ts +2 -0
  186. package/src/eventDispatchers/keyboardEventHandlers/keyDown.ts +7 -1
  187. package/src/index.ts +2 -0
  188. package/src/stateManagement/annotation/annotationState.ts +3 -0
  189. package/src/tools/annotation/AngleTool.ts +0 -1
  190. package/src/tools/annotation/BidirectionalTool.ts +9 -5
  191. package/src/tools/annotation/LengthTool.ts +6 -8
  192. package/src/tools/annotation/ProbeTool.ts +31 -7
  193. package/src/tools/annotation/UltrasoundDirectionalTool.ts +916 -0
  194. package/src/tools/index.ts +2 -0
  195. package/src/types/ToolSpecificAnnotationTypes.ts +29 -0
  196. package/src/types/index.ts +2 -0
  197. package/src/utilities/contours/AnnotationToPointData.ts +61 -0
  198. package/src/utilities/contours/RectangleROIStartEndThreshold.ts +60 -0
  199. package/src/utilities/contours/contourFinder.ts +78 -0
  200. package/src/utilities/contours/detectContourHoles.ts +147 -0
  201. package/src/utilities/contours/generateContourSetsFromLabelmap.ts +160 -0
  202. package/src/utilities/contours/index.ts +14 -0
  203. package/src/utilities/contours/mergePoints.ts +108 -0
  204. package/src/utilities/getCalibratedUnits.ts +203 -7
  205. package/src/utilities/index.ts +2 -0
  206. package/src/utilities/segmentation/contourAndFindLargestBidirectional.ts +46 -0
  207. package/src/utilities/segmentation/createBidirectionalToolData.ts +68 -0
  208. package/src/utilities/segmentation/findLargestBidirectional.ts +159 -0
  209. package/src/utilities/segmentation/index.ts +6 -0
  210. package/src/utilities/segmentation/isLineInSegment.ts +84 -0
  211. package/src/utilities/segmentation/segmentContourAction.ts +169 -0
@@ -0,0 +1,916 @@
1
+ import { Events } from '../../enums';
2
+ import {
3
+ getEnabledElement,
4
+ triggerEvent,
5
+ eventTarget,
6
+ utilities as csUtils,
7
+ StackViewport,
8
+ } from '@cornerstonejs/core';
9
+ import type { Types } from '@cornerstonejs/core';
10
+
11
+ import { AnnotationTool } from '../base';
12
+ import throttle from '../../utilities/throttle';
13
+ import {
14
+ addAnnotation,
15
+ getAnnotations,
16
+ removeAnnotation,
17
+ } from '../../stateManagement/annotation/annotationState';
18
+ import { UltrasoundDirectionalAnnotation } from '../../types/ToolSpecificAnnotationTypes';
19
+
20
+ import {
21
+ drawHandle as drawHandleSvg,
22
+ drawLine as drawLineSvg,
23
+ drawLinkedTextBox as drawLinkedTextBoxSvg,
24
+ } from '../../drawingSvg';
25
+ import { state } from '../../store';
26
+ import { getViewportIdsWithToolToRender } from '../../utilities/viewportFilters';
27
+ import { roundNumber } from '../../utilities';
28
+ import { distanceToPoint } from '../../utilities/math/point';
29
+ import triggerAnnotationRenderForViewportIds from '../../utilities/triggerAnnotationRenderForViewportIds';
30
+ import {
31
+ AnnotationCompletedEventDetail,
32
+ AnnotationModifiedEventDetail,
33
+ } from '../../types/EventTypes';
34
+
35
+ import {
36
+ resetElementCursor,
37
+ hideElementCursor,
38
+ } from '../../cursors/elementCursor';
39
+
40
+ import {
41
+ EventTypes,
42
+ ToolHandle,
43
+ TextBoxHandle,
44
+ PublicToolProps,
45
+ ToolProps,
46
+ SVGDrawingHelper,
47
+ Annotation,
48
+ InteractionTypes,
49
+ } from '../../types';
50
+ import { StyleSpecifier } from '../../types/AnnotationStyle';
51
+ import { getCalibratedProbeUnitsAndValue } from '../../utilities/getCalibratedUnits';
52
+ const { transformWorldToIndex } = csUtils;
53
+
54
+ /**
55
+ * The `UltrasoundDirectionalTool` class is a tool for creating directional ultrasound annotations.
56
+ * It allows users to draw lines and measure distances between two points in the image.
57
+ * It automatically calculates the distance based on the relevant unit of measurement.
58
+ */
59
+ class UltrasoundDirectionalTool extends AnnotationTool {
60
+ static toolName;
61
+
62
+ public touchDragCallback: any;
63
+ public mouseDragCallback: any;
64
+ startedDrawing: boolean;
65
+ _throttledCalculateCachedStats: any;
66
+ editData: {
67
+ annotation: any;
68
+ viewportIdsToRender: string[];
69
+ handleIndex?: number;
70
+ movingTextBox?: boolean;
71
+ newAnnotation?: boolean;
72
+ hasMoved?: boolean;
73
+ } | null;
74
+ isDrawing: boolean;
75
+ isHandleOutsideImage: boolean;
76
+
77
+ constructor(
78
+ toolProps: PublicToolProps = {},
79
+ defaultToolProps: ToolProps = {
80
+ supportedInteractionTypes: ['Mouse', 'Touch'],
81
+ configuration: {
82
+ shadow: true,
83
+ preventHandleOutsideImage: false,
84
+ getTextLines: defaultGetTextLines,
85
+ /**
86
+ * Determines whether both horizontal and vertical distances should be displayed
87
+ * in the text lines when generating annotations' measurement information.
88
+ */
89
+ displayBothAxesDistances: false,
90
+ },
91
+ }
92
+ ) {
93
+ super(toolProps, defaultToolProps);
94
+
95
+ this._throttledCalculateCachedStats = throttle(
96
+ this._calculateCachedStats,
97
+ 100,
98
+ { trailing: true }
99
+ );
100
+ }
101
+
102
+ /**
103
+ * Based on the current position of the mouse and the current imageId to create
104
+ * a Ultrasound Directional Tool and store it in the annotationManager
105
+ *
106
+ * @param evt - EventTypes.InteractionEventType
107
+ * @returns The annotation object.
108
+ */
109
+ addNewAnnotation = (
110
+ evt: EventTypes.InteractionEventType
111
+ ): UltrasoundDirectionalAnnotation => {
112
+ if (this.startedDrawing) {
113
+ return;
114
+ }
115
+
116
+ this.startedDrawing = true;
117
+ const eventDetail = evt.detail;
118
+ const { currentPoints, element } = eventDetail;
119
+
120
+ const worldPos = currentPoints.world;
121
+ const enabledElement = getEnabledElement(element);
122
+ const { viewport, renderingEngine } = enabledElement;
123
+
124
+ if (!(viewport instanceof StackViewport)) {
125
+ throw new Error(
126
+ 'UltrasoundDirectionalTool can only be used on a StackViewport'
127
+ );
128
+ }
129
+
130
+ hideElementCursor(element);
131
+ this.isDrawing = true;
132
+
133
+ const camera = viewport.getCamera();
134
+ const { viewPlaneNormal, viewUp } = camera;
135
+
136
+ const referencedImageId = this.getReferencedImageId(
137
+ viewport,
138
+ worldPos,
139
+ viewPlaneNormal,
140
+ viewUp
141
+ );
142
+
143
+ const FrameOfReferenceUID = viewport.getFrameOfReferenceUID();
144
+
145
+ const annotation = {
146
+ highlighted: true,
147
+ invalidated: true,
148
+ metadata: {
149
+ toolName: this.getToolName(),
150
+ viewPlaneNormal: <Types.Point3>[...viewPlaneNormal],
151
+ viewUp: <Types.Point3>[...viewUp],
152
+ FrameOfReferenceUID,
153
+ referencedImageId,
154
+ },
155
+ data: {
156
+ handles: {
157
+ points: [<Types.Point3>[...worldPos], <Types.Point3>[...worldPos]],
158
+ activeHandleIndex: null,
159
+ textBox: {
160
+ hasMoved: false,
161
+ worldPosition: <Types.Point3>[0, 0, 0],
162
+ worldBoundingBox: {
163
+ topLeft: <Types.Point3>[0, 0, 0],
164
+ topRight: <Types.Point3>[0, 0, 0],
165
+ bottomLeft: <Types.Point3>[0, 0, 0],
166
+ bottomRight: <Types.Point3>[0, 0, 0],
167
+ },
168
+ },
169
+ },
170
+ label: '',
171
+ cachedStats: {},
172
+ },
173
+ };
174
+
175
+ addAnnotation(annotation, element);
176
+
177
+ const viewportIdsToRender = getViewportIdsWithToolToRender(
178
+ element,
179
+ this.getToolName()
180
+ );
181
+
182
+ this.editData = {
183
+ annotation,
184
+ viewportIdsToRender,
185
+ handleIndex: 1,
186
+ movingTextBox: false,
187
+ newAnnotation: true,
188
+ hasMoved: false,
189
+ };
190
+ this._activateDraw(element);
191
+
192
+ evt.preventDefault();
193
+
194
+ triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender);
195
+
196
+ return annotation;
197
+ };
198
+
199
+ /**
200
+ * It returns if the canvas point is near the provided length annotation in the provided
201
+ * element or not. A proximity is passed to the function to determine the
202
+ * proximity of the point to the annotation in number of pixels.
203
+ *
204
+ * @param element - HTML Element
205
+ * @param annotation - Annotation
206
+ * @param canvasCoords - Canvas coordinates
207
+ * @param proximity - Proximity to tool to consider
208
+ * @returns Boolean, whether the canvas point is near tool
209
+ */
210
+ isPointNearTool = (
211
+ element: HTMLDivElement,
212
+ annotation: UltrasoundDirectionalAnnotation,
213
+ canvasCoords: Types.Point2,
214
+ proximity: number
215
+ ): boolean => {
216
+ return false;
217
+ };
218
+
219
+ toolSelectedCallback(
220
+ evt: EventTypes.InteractionEventType,
221
+ annotation: Annotation,
222
+ interactionType: InteractionTypes,
223
+ canvasCoords?: Types.Point2
224
+ ): void {
225
+ return;
226
+ }
227
+
228
+ handleSelectedCallback(
229
+ evt: EventTypes.InteractionEventType,
230
+ annotation: UltrasoundDirectionalAnnotation,
231
+ handle: ToolHandle
232
+ ): void {
233
+ const eventDetail = evt.detail;
234
+ const { element } = eventDetail;
235
+ const { data } = annotation;
236
+
237
+ annotation.highlighted = true;
238
+
239
+ const viewportIdsToRender = getViewportIdsWithToolToRender(
240
+ element,
241
+ this.getToolName()
242
+ );
243
+
244
+ let movingTextBox = false;
245
+ let handleIndex;
246
+ if ((handle as TextBoxHandle).worldPosition) {
247
+ movingTextBox = true;
248
+ } else {
249
+ handleIndex = data.handles.points.findIndex((p) => p === handle);
250
+ }
251
+
252
+ // Find viewports to render on drag.
253
+
254
+ this.editData = {
255
+ handleIndex,
256
+ annotation,
257
+ viewportIdsToRender,
258
+ };
259
+ this._activateModify(element);
260
+
261
+ hideElementCursor(element);
262
+
263
+ const enabledElement = getEnabledElement(element);
264
+ const { renderingEngine } = enabledElement;
265
+
266
+ triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender);
267
+
268
+ evt.preventDefault();
269
+ }
270
+
271
+ _endCallback = (evt: EventTypes.InteractionEventType): void => {
272
+ const eventDetail = evt.detail;
273
+ const { element } = eventDetail;
274
+
275
+ const { annotation, viewportIdsToRender, newAnnotation, hasMoved } =
276
+ this.editData;
277
+
278
+ const { data } = annotation;
279
+ if (newAnnotation && !hasMoved) {
280
+ // when user starts the drawing by click, and moving the mouse, instead
281
+ // of click and drag
282
+ return;
283
+ }
284
+
285
+ // If preventing new measurement means we are in the middle of an existing measurement
286
+ // we shouldn't deactivate modify or draw
287
+ if (this.startedDrawing && data.handles.points.length === 1) {
288
+ // adds the last point to the measurement
289
+ this.editData.handleIndex = 1;
290
+ return;
291
+ }
292
+
293
+ this.startedDrawing = false;
294
+ data.handles.activeHandleIndex = null;
295
+
296
+ this._deactivateModify(element);
297
+ this._deactivateDraw(element);
298
+ resetElementCursor(element);
299
+
300
+ const enabledElement = getEnabledElement(element);
301
+ const { renderingEngine } = enabledElement;
302
+
303
+ if (
304
+ this.isHandleOutsideImage &&
305
+ this.configuration.preventHandleOutsideImage
306
+ ) {
307
+ removeAnnotation(annotation.annotationUID);
308
+ }
309
+
310
+ triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender);
311
+
312
+ if (newAnnotation) {
313
+ const eventType = Events.ANNOTATION_COMPLETED;
314
+
315
+ const eventDetail: AnnotationCompletedEventDetail = {
316
+ annotation,
317
+ };
318
+
319
+ triggerEvent(eventTarget, eventType, eventDetail);
320
+ }
321
+
322
+ this.editData = null;
323
+ this.isDrawing = false;
324
+ };
325
+
326
+ _dragCallback = (evt: EventTypes.InteractionEventType): void => {
327
+ this.isDrawing = true;
328
+ const eventDetail = evt.detail;
329
+ const { element } = eventDetail;
330
+
331
+ const { annotation, viewportIdsToRender, handleIndex, movingTextBox } =
332
+ this.editData;
333
+ const { data } = annotation;
334
+
335
+ if (movingTextBox) {
336
+ // Drag mode - moving text box
337
+ const { deltaPoints } = eventDetail as EventTypes.MouseDragEventDetail;
338
+ const worldPosDelta = deltaPoints.world;
339
+
340
+ const { textBox } = data.handles;
341
+ const { worldPosition } = textBox;
342
+
343
+ worldPosition[0] += worldPosDelta[0];
344
+ worldPosition[1] += worldPosDelta[1];
345
+ worldPosition[2] += worldPosDelta[2];
346
+
347
+ textBox.hasMoved = true;
348
+ } else if (handleIndex === undefined) {
349
+ // Drag mode - moving handle
350
+ const { deltaPoints } = eventDetail as EventTypes.MouseDragEventDetail;
351
+ const worldPosDelta = deltaPoints.world;
352
+
353
+ const points = data.handles.points;
354
+
355
+ points.forEach((point) => {
356
+ point[0] += worldPosDelta[0];
357
+ point[1] += worldPosDelta[1];
358
+ point[2] += worldPosDelta[2];
359
+ });
360
+ annotation.invalidated = true;
361
+ } else {
362
+ // Move mode - after double click, and mouse move to draw
363
+ const { currentPoints } = eventDetail;
364
+ const worldPos = currentPoints.world;
365
+
366
+ data.handles.points[handleIndex] = [...worldPos];
367
+ annotation.invalidated = true;
368
+ }
369
+
370
+ this.editData.hasMoved = true;
371
+
372
+ const enabledElement = getEnabledElement(element);
373
+ const { renderingEngine } = enabledElement;
374
+
375
+ triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender);
376
+ };
377
+
378
+ cancel = (element: HTMLDivElement) => {
379
+ // If it is mid-draw or mid-modify
380
+ if (this.isDrawing) {
381
+ this.isDrawing = false;
382
+ this._deactivateDraw(element);
383
+ this._deactivateModify(element);
384
+ resetElementCursor(element);
385
+
386
+ const { annotation, viewportIdsToRender, newAnnotation } = this.editData;
387
+ const { data } = annotation;
388
+
389
+ annotation.highlighted = false;
390
+ data.handles.activeHandleIndex = null;
391
+
392
+ const enabledElement = getEnabledElement(element);
393
+ const { renderingEngine } = enabledElement;
394
+
395
+ triggerAnnotationRenderForViewportIds(
396
+ renderingEngine,
397
+ viewportIdsToRender
398
+ );
399
+
400
+ if (newAnnotation) {
401
+ const eventType = Events.ANNOTATION_COMPLETED;
402
+
403
+ const eventDetail: AnnotationCompletedEventDetail = {
404
+ annotation,
405
+ };
406
+
407
+ triggerEvent(eventTarget, eventType, eventDetail);
408
+ }
409
+
410
+ this.editData = null;
411
+ this.startedDrawing = false;
412
+ return annotation.annotationUID;
413
+ }
414
+ };
415
+
416
+ _activateModify = (element: HTMLDivElement) => {
417
+ state.isInteractingWithTool = true;
418
+
419
+ element.addEventListener(
420
+ Events.MOUSE_UP,
421
+ this._endCallback as EventListener
422
+ );
423
+ element.addEventListener(
424
+ Events.MOUSE_DRAG,
425
+ this._dragCallback as EventListener
426
+ );
427
+ element.addEventListener(
428
+ Events.MOUSE_CLICK,
429
+ this._endCallback as EventListener
430
+ );
431
+
432
+ element.addEventListener(
433
+ Events.TOUCH_TAP,
434
+ this._endCallback as EventListener
435
+ );
436
+ element.addEventListener(
437
+ Events.TOUCH_END,
438
+ this._endCallback as EventListener
439
+ );
440
+ element.addEventListener(
441
+ Events.TOUCH_DRAG,
442
+ this._dragCallback as EventListener
443
+ );
444
+ };
445
+
446
+ _deactivateModify = (element: HTMLDivElement) => {
447
+ state.isInteractingWithTool = false;
448
+
449
+ element.removeEventListener(
450
+ Events.MOUSE_UP,
451
+ this._endCallback as EventListener
452
+ );
453
+ element.removeEventListener(
454
+ Events.MOUSE_DRAG,
455
+ this._dragCallback as EventListener
456
+ );
457
+ element.removeEventListener(
458
+ Events.MOUSE_CLICK,
459
+ this._endCallback as EventListener
460
+ );
461
+ element.removeEventListener(
462
+ Events.TOUCH_TAP,
463
+ this._endCallback as EventListener
464
+ );
465
+ element.removeEventListener(
466
+ Events.TOUCH_END,
467
+ this._endCallback as EventListener
468
+ );
469
+ element.removeEventListener(
470
+ Events.TOUCH_DRAG,
471
+ this._dragCallback as EventListener
472
+ );
473
+ };
474
+
475
+ _activateDraw = (element: HTMLDivElement) => {
476
+ state.isInteractingWithTool = true;
477
+
478
+ element.addEventListener(
479
+ Events.MOUSE_UP,
480
+ this._endCallback as EventListener
481
+ );
482
+ element.addEventListener(
483
+ Events.MOUSE_DRAG,
484
+ this._dragCallback as EventListener
485
+ );
486
+ element.addEventListener(
487
+ Events.MOUSE_MOVE,
488
+ this._dragCallback as EventListener
489
+ );
490
+ element.addEventListener(
491
+ Events.MOUSE_CLICK,
492
+ this._endCallback as EventListener
493
+ );
494
+
495
+ element.addEventListener(
496
+ Events.TOUCH_TAP,
497
+ this._endCallback as EventListener
498
+ );
499
+ element.addEventListener(
500
+ Events.TOUCH_END,
501
+ this._endCallback as EventListener
502
+ );
503
+ element.addEventListener(
504
+ Events.TOUCH_DRAG,
505
+ this._dragCallback as EventListener
506
+ );
507
+ };
508
+
509
+ _deactivateDraw = (element: HTMLDivElement) => {
510
+ state.isInteractingWithTool = false;
511
+
512
+ element.removeEventListener(
513
+ Events.MOUSE_UP,
514
+ this._endCallback as EventListener
515
+ );
516
+ element.removeEventListener(
517
+ Events.MOUSE_DRAG,
518
+ this._dragCallback as EventListener
519
+ );
520
+ element.removeEventListener(
521
+ Events.MOUSE_MOVE,
522
+ this._dragCallback as EventListener
523
+ );
524
+ element.removeEventListener(
525
+ Events.MOUSE_CLICK,
526
+ this._endCallback as EventListener
527
+ );
528
+
529
+ element.removeEventListener(
530
+ Events.TOUCH_TAP,
531
+ this._endCallback as EventListener
532
+ );
533
+ element.removeEventListener(
534
+ Events.TOUCH_END,
535
+ this._endCallback as EventListener
536
+ );
537
+ element.removeEventListener(
538
+ Events.TOUCH_DRAG,
539
+ this._dragCallback as EventListener
540
+ );
541
+ };
542
+
543
+ /**
544
+ * it is used to draw the length annotation in each
545
+ * request animation frame. It calculates the updated cached statistics if
546
+ * data is invalidated and cache it.
547
+ *
548
+ * @param enabledElement - The Cornerstone's enabledElement.
549
+ * @param svgDrawingHelper - The svgDrawingHelper providing the context for drawing.
550
+ */
551
+ renderAnnotation = (
552
+ enabledElement: Types.IEnabledElement,
553
+ svgDrawingHelper: SVGDrawingHelper
554
+ ): boolean => {
555
+ let renderStatus = false;
556
+
557
+ const { viewport } = enabledElement;
558
+ const { element } = viewport;
559
+
560
+ let annotations = getAnnotations(this.getToolName(), element);
561
+
562
+ // Todo: We don't need this anymore, filtering happens in triggerAnnotationRender
563
+ if (!annotations?.length) {
564
+ return renderStatus;
565
+ }
566
+
567
+ annotations = this.filterInteractableAnnotationsForElement(
568
+ element,
569
+ annotations
570
+ );
571
+
572
+ if (!annotations?.length) {
573
+ return renderStatus;
574
+ }
575
+
576
+ const targetId = this.getTargetId(viewport);
577
+ const renderingEngine = viewport.getRenderingEngine();
578
+
579
+ const styleSpecifier: StyleSpecifier = {
580
+ toolGroupId: this.toolGroupId,
581
+ toolName: this.getToolName(),
582
+ viewportId: enabledElement.viewport.id,
583
+ };
584
+
585
+ // Draw SVG
586
+ for (let i = 0; i < annotations.length; i++) {
587
+ const annotation = annotations[i] as UltrasoundDirectionalAnnotation;
588
+ const { annotationUID, data } = annotation;
589
+ const { points } = data.handles;
590
+
591
+ styleSpecifier.annotationUID = annotationUID;
592
+
593
+ const color = this.getStyle('color', styleSpecifier, annotation);
594
+
595
+ const canvasCoordinates = points.map((p) => viewport.worldToCanvas(p));
596
+
597
+ // WE HAVE TO CACHE STATS BEFORE FETCHING TEXT
598
+ if (
599
+ !data.cachedStats[targetId] ||
600
+ data.cachedStats[targetId].xValues == null
601
+ ) {
602
+ data.cachedStats[targetId] = {
603
+ xValues: [0, 0],
604
+ yValues: [0, 0],
605
+ isHorizontal: false,
606
+ units: [''],
607
+ isUnitless: false,
608
+ };
609
+
610
+ this._calculateCachedStats(annotation, renderingEngine, enabledElement);
611
+ } else if (annotation.invalidated) {
612
+ this._throttledCalculateCachedStats(
613
+ annotation,
614
+ renderingEngine,
615
+ enabledElement
616
+ );
617
+ }
618
+
619
+ // If rendering engine has been destroyed while rendering
620
+ if (!viewport.getRenderingEngine()) {
621
+ console.warn('Rendering Engine has been destroyed');
622
+ return renderStatus;
623
+ }
624
+
625
+ // draw first point
626
+ let handleGroupUID = '0';
627
+ drawHandleSvg(
628
+ svgDrawingHelper,
629
+ annotationUID,
630
+ handleGroupUID,
631
+ canvasCoordinates[0],
632
+ {
633
+ color,
634
+ },
635
+ 0
636
+ );
637
+
638
+ renderStatus = true;
639
+
640
+ if (canvasCoordinates.length !== 2) {
641
+ return renderStatus;
642
+ }
643
+
644
+ handleGroupUID = '1';
645
+ drawHandleSvg(
646
+ svgDrawingHelper,
647
+ annotationUID,
648
+ handleGroupUID,
649
+ canvasCoordinates[1],
650
+ {
651
+ color,
652
+ },
653
+ 1
654
+ );
655
+
656
+ const isUnitless = data.cachedStats[targetId].isUnitless;
657
+
658
+ if (!isUnitless) {
659
+ const canvasPoint1 = canvasCoordinates[0];
660
+ const canvasPoint2 = canvasCoordinates[1];
661
+
662
+ const canvasDeltaY = canvasPoint2[1] - canvasPoint1[1];
663
+ const canvasDeltaX = canvasPoint2[0] - canvasPoint1[0];
664
+
665
+ const isHorizontal = data.cachedStats[targetId].isHorizontal;
666
+
667
+ // then for the third point we need to go from first point towards
668
+ // the second point (it can be left or right in the horizontal orientation)
669
+ // or up or down in the vertical orientation, and only add
670
+ // the delta y to the x or y coordinate of the first point
671
+ let projectedPointCanvas = [0, 0] as Types.Point2;
672
+ if (isHorizontal) {
673
+ projectedPointCanvas = [
674
+ canvasPoint1[0] + canvasDeltaX,
675
+ canvasPoint1[1],
676
+ ];
677
+ } else {
678
+ projectedPointCanvas = [
679
+ canvasPoint1[0],
680
+ canvasPoint1[1] + canvasDeltaY,
681
+ ];
682
+ }
683
+
684
+ // create a line from the first point to the third point
685
+ let dataId = `${annotationUID}-line-1`;
686
+ let lineUID = '1';
687
+ drawLineSvg(
688
+ svgDrawingHelper,
689
+ annotationUID,
690
+ lineUID,
691
+ canvasCoordinates[0],
692
+ projectedPointCanvas,
693
+ {
694
+ color,
695
+ width: 1,
696
+ shadow: this.configuration.shadow,
697
+ },
698
+ dataId
699
+ );
700
+
701
+ // draw another line from first point to the projected one
702
+ dataId = `${annotationUID}-line-2`;
703
+ lineUID = '2';
704
+
705
+ drawLineSvg(
706
+ svgDrawingHelper,
707
+ annotationUID,
708
+ lineUID,
709
+ canvasCoordinates[1],
710
+ projectedPointCanvas,
711
+ {
712
+ color,
713
+ width: 1,
714
+ lineDash: [1, 1],
715
+ shadow: this.configuration.shadow,
716
+ },
717
+ dataId
718
+ );
719
+ } else {
720
+ // draw straight line between the two points
721
+ const dataId = `${annotationUID}-line-1`;
722
+ const lineUID = '1';
723
+ drawLineSvg(
724
+ svgDrawingHelper,
725
+ annotationUID,
726
+ lineUID,
727
+ canvasCoordinates[0],
728
+ canvasCoordinates[1],
729
+ {
730
+ color,
731
+ width: 1,
732
+ shadow: this.configuration.shadow,
733
+ },
734
+ dataId
735
+ );
736
+ }
737
+
738
+ // draw another line from first point to the
739
+
740
+ const options = this.getLinkedTextBoxStyle(styleSpecifier, annotation);
741
+ if (!options.visibility) {
742
+ data.handles.textBox = {
743
+ hasMoved: false,
744
+ worldPosition: <Types.Point3>[0, 0, 0],
745
+ worldBoundingBox: {
746
+ topLeft: <Types.Point3>[0, 0, 0],
747
+ topRight: <Types.Point3>[0, 0, 0],
748
+ bottomLeft: <Types.Point3>[0, 0, 0],
749
+ bottomRight: <Types.Point3>[0, 0, 0],
750
+ },
751
+ };
752
+ continue;
753
+ }
754
+
755
+ const textLines = this.configuration.getTextLines(
756
+ data,
757
+ targetId,
758
+ this.configuration
759
+ );
760
+
761
+ if (!data.handles.textBox.hasMoved) {
762
+ // linked to the vertex by default
763
+ const canvasTextBoxCoords = canvasCoordinates[1];
764
+
765
+ data.handles.textBox.worldPosition =
766
+ viewport.canvasToWorld(canvasTextBoxCoords);
767
+ }
768
+
769
+ const textBoxPosition = viewport.worldToCanvas(
770
+ data.handles.textBox.worldPosition
771
+ );
772
+
773
+ const textBoxUID = '1';
774
+ const boundingBox = drawLinkedTextBoxSvg(
775
+ svgDrawingHelper,
776
+ annotationUID,
777
+ textBoxUID,
778
+ textLines,
779
+ textBoxPosition,
780
+ canvasCoordinates,
781
+ {},
782
+ options
783
+ );
784
+
785
+ const { x: left, y: top, width, height } = boundingBox;
786
+
787
+ data.handles.textBox.worldBoundingBox = {
788
+ topLeft: viewport.canvasToWorld([left, top]),
789
+ topRight: viewport.canvasToWorld([left + width, top]),
790
+ bottomLeft: viewport.canvasToWorld([left, top + height]),
791
+ bottomRight: viewport.canvasToWorld([left + width, top + height]),
792
+ };
793
+ }
794
+
795
+ return renderStatus;
796
+ };
797
+
798
+ _calculateCachedStats(annotation, renderingEngine, enabledElement) {
799
+ const data = annotation.data;
800
+ const { viewportId, renderingEngineId } = enabledElement;
801
+
802
+ // Until we have all two anchors bail out
803
+ if (data.handles.points.length !== 2) {
804
+ return;
805
+ }
806
+
807
+ const { cachedStats } = data;
808
+ const targetIds = Object.keys(cachedStats);
809
+
810
+ for (let i = 0; i < targetIds.length; i++) {
811
+ const targetId = targetIds[i];
812
+
813
+ const image = this.getTargetIdImage(targetId, renderingEngine);
814
+
815
+ // If image does not exists for the targetId, skip. This can be due
816
+ // to various reasons such as if the target was a volumeViewport, and
817
+ // the volumeViewport has been decached in the meantime.
818
+ if (!image) {
819
+ continue;
820
+ }
821
+
822
+ const { imageData } = image;
823
+
824
+ const worldPos1 = data.handles.points[0];
825
+ const worldPos2 = data.handles.points[1];
826
+
827
+ const imageIndex1 = transformWorldToIndex(imageData, worldPos1);
828
+ const imageIndex2 = transformWorldToIndex(imageData, worldPos2);
829
+
830
+ const { values: values1, units: units1 } =
831
+ getCalibratedProbeUnitsAndValue(image, [imageIndex1]);
832
+ const { values: values2, units: units2 } =
833
+ getCalibratedProbeUnitsAndValue(image, [imageIndex2]);
834
+
835
+ let xValues, yValues, units, isHorizontal;
836
+ let isUnitless = false;
837
+ if (
838
+ units1[0] !== units2[0] ||
839
+ units1[1] !== units2[1] ||
840
+ (units1[0] === 'raw' && units2[0] === 'raw')
841
+ ) {
842
+ // if units are not the same, we cannot calculate the diff
843
+ // so we just report the px distance
844
+ const value = distanceToPoint(worldPos1, worldPos2);
845
+
846
+ xValues = [value, 0];
847
+ yValues = [value, 0];
848
+ units = ['px'];
849
+ isUnitless = true;
850
+ } else {
851
+ const canvasPoint1 = enabledElement.viewport.worldToCanvas(worldPos1);
852
+ const canvasPoint2 = enabledElement.viewport.worldToCanvas(worldPos2);
853
+
854
+ const canvasDeltaY = canvasPoint2[1] - canvasPoint1[1];
855
+ const canvasDeltaX = canvasPoint2[0] - canvasPoint1[0];
856
+
857
+ isHorizontal = Math.abs(canvasDeltaX) > Math.abs(canvasDeltaY);
858
+ xValues = [values1[0], values2[0]];
859
+ yValues = [values1[1], values2[1]];
860
+
861
+ units = [units1[0], units1[1]];
862
+ }
863
+
864
+ cachedStats[targetId] = {
865
+ xValues,
866
+ yValues,
867
+ isHorizontal,
868
+ units,
869
+ isUnitless,
870
+ };
871
+ }
872
+
873
+ annotation.invalidated = false;
874
+
875
+ // Dispatching annotation modified
876
+ const eventType = Events.ANNOTATION_MODIFIED;
877
+
878
+ const eventDetail: AnnotationModifiedEventDetail = {
879
+ annotation,
880
+ viewportId,
881
+ renderingEngineId,
882
+ };
883
+ triggerEvent(eventTarget, eventType, eventDetail);
884
+
885
+ return cachedStats;
886
+ }
887
+ }
888
+
889
+ function defaultGetTextLines(data, targetId, configuration): string[] {
890
+ const cachedStats = data.cachedStats[targetId];
891
+ const { xValues, yValues, units, isUnitless, isHorizontal } = cachedStats;
892
+
893
+ if (isUnitless) {
894
+ return [`${roundNumber(xValues[0])} px`];
895
+ }
896
+
897
+ if (configuration.displayBothAxesDistances) {
898
+ const dist1 = Math.abs(xValues[1] - xValues[0]);
899
+ const dist2 = Math.abs(yValues[1] - yValues[0]);
900
+ return [
901
+ `${roundNumber(dist1)} ${units[0]}`,
902
+ `${roundNumber(dist2)} ${units[1]}`,
903
+ ];
904
+ }
905
+
906
+ if (isHorizontal) {
907
+ const dist = Math.abs(xValues[1] - xValues[0]);
908
+ return [`${roundNumber(dist)} ${units[0]}`];
909
+ } else {
910
+ const dist = Math.abs(yValues[1] - yValues[0]);
911
+ return [`${roundNumber(dist)} ${units[1]}`];
912
+ }
913
+ }
914
+
915
+ UltrasoundDirectionalTool.toolName = 'UltrasoundDirectionalTool';
916
+ export default UltrasoundDirectionalTool;