@cornerstonejs/tools 1.14.3 → 1.15.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 (78) hide show
  1. package/dist/cjs/stateManagement/annotation/config/ToolStyle.js +1 -0
  2. package/dist/cjs/stateManagement/annotation/config/ToolStyle.js.map +1 -1
  3. package/dist/cjs/store/SynchronizerManager/Synchronizer.js +10 -2
  4. package/dist/cjs/store/SynchronizerManager/Synchronizer.js.map +1 -1
  5. package/dist/cjs/synchronizers/callbacks/stackImageSyncCallback.js +25 -22
  6. package/dist/cjs/synchronizers/callbacks/stackImageSyncCallback.js.map +1 -1
  7. package/dist/cjs/synchronizers/callbacks/voiSyncCallback.js +1 -1
  8. package/dist/cjs/synchronizers/callbacks/voiSyncCallback.js.map +1 -1
  9. package/dist/cjs/tools/annotation/AngleTool.js +15 -1
  10. package/dist/cjs/tools/annotation/AngleTool.js.map +1 -1
  11. package/dist/cjs/tools/annotation/ArrowAnnotateTool.js +15 -1
  12. package/dist/cjs/tools/annotation/ArrowAnnotateTool.js.map +1 -1
  13. package/dist/cjs/tools/annotation/BidirectionalTool.js +15 -1
  14. package/dist/cjs/tools/annotation/BidirectionalTool.js.map +1 -1
  15. package/dist/cjs/tools/annotation/CircleROITool.js +15 -1
  16. package/dist/cjs/tools/annotation/CircleROITool.js.map +1 -1
  17. package/dist/cjs/tools/annotation/CobbAngleTool.js +15 -1
  18. package/dist/cjs/tools/annotation/CobbAngleTool.js.map +1 -1
  19. package/dist/cjs/tools/annotation/EllipticalROITool.js +15 -1
  20. package/dist/cjs/tools/annotation/EllipticalROITool.js.map +1 -1
  21. package/dist/cjs/tools/annotation/LengthTool.js +15 -1
  22. package/dist/cjs/tools/annotation/LengthTool.js.map +1 -1
  23. package/dist/cjs/tools/annotation/PlanarFreehandROITool.js +10 -6
  24. package/dist/cjs/tools/annotation/PlanarFreehandROITool.js.map +1 -1
  25. package/dist/cjs/tools/annotation/ProbeTool.js +5 -1
  26. package/dist/cjs/tools/annotation/ProbeTool.js.map +1 -1
  27. package/dist/cjs/tools/annotation/RectangleROITool.js +15 -1
  28. package/dist/cjs/tools/annotation/RectangleROITool.js.map +1 -1
  29. package/dist/cjs/tools/base/AnnotationTool.js +1 -0
  30. package/dist/cjs/tools/base/AnnotationTool.js.map +1 -1
  31. package/dist/esm/stateManagement/annotation/config/ToolStyle.js +1 -0
  32. package/dist/esm/stateManagement/annotation/config/ToolStyle.js.map +1 -1
  33. package/dist/esm/store/SynchronizerManager/Synchronizer.js +10 -2
  34. package/dist/esm/store/SynchronizerManager/Synchronizer.js.map +1 -1
  35. package/dist/esm/synchronizers/callbacks/stackImageSyncCallback.js +26 -23
  36. package/dist/esm/synchronizers/callbacks/stackImageSyncCallback.js.map +1 -1
  37. package/dist/esm/synchronizers/callbacks/voiSyncCallback.js +1 -1
  38. package/dist/esm/synchronizers/callbacks/voiSyncCallback.js.map +1 -1
  39. package/dist/esm/tools/annotation/AngleTool.js +15 -1
  40. package/dist/esm/tools/annotation/AngleTool.js.map +1 -1
  41. package/dist/esm/tools/annotation/ArrowAnnotateTool.js +15 -1
  42. package/dist/esm/tools/annotation/ArrowAnnotateTool.js.map +1 -1
  43. package/dist/esm/tools/annotation/BidirectionalTool.js +15 -1
  44. package/dist/esm/tools/annotation/BidirectionalTool.js.map +1 -1
  45. package/dist/esm/tools/annotation/CircleROITool.js +15 -1
  46. package/dist/esm/tools/annotation/CircleROITool.js.map +1 -1
  47. package/dist/esm/tools/annotation/CobbAngleTool.js +15 -1
  48. package/dist/esm/tools/annotation/CobbAngleTool.js.map +1 -1
  49. package/dist/esm/tools/annotation/EllipticalROITool.js +15 -1
  50. package/dist/esm/tools/annotation/EllipticalROITool.js.map +1 -1
  51. package/dist/esm/tools/annotation/LengthTool.js +15 -1
  52. package/dist/esm/tools/annotation/LengthTool.js.map +1 -1
  53. package/dist/esm/tools/annotation/PlanarFreehandROITool.js +10 -6
  54. package/dist/esm/tools/annotation/PlanarFreehandROITool.js.map +1 -1
  55. package/dist/esm/tools/annotation/ProbeTool.js +5 -1
  56. package/dist/esm/tools/annotation/ProbeTool.js.map +1 -1
  57. package/dist/esm/tools/annotation/RectangleROITool.js +15 -1
  58. package/dist/esm/tools/annotation/RectangleROITool.js.map +1 -1
  59. package/dist/esm/tools/base/AnnotationTool.js +1 -0
  60. package/dist/esm/tools/base/AnnotationTool.js.map +1 -1
  61. package/dist/umd/index.js +1 -1
  62. package/dist/umd/index.js.map +1 -1
  63. package/package.json +3 -3
  64. package/src/stateManagement/annotation/config/ToolStyle.ts +1 -0
  65. package/src/store/SynchronizerManager/Synchronizer.ts +16 -8
  66. package/src/synchronizers/callbacks/stackImageSyncCallback.ts +63 -68
  67. package/src/synchronizers/callbacks/voiSyncCallback.ts +1 -1
  68. package/src/tools/annotation/AngleTool.ts +16 -1
  69. package/src/tools/annotation/ArrowAnnotateTool.ts +16 -1
  70. package/src/tools/annotation/BidirectionalTool.ts +17 -2
  71. package/src/tools/annotation/CircleROITool.ts +16 -1
  72. package/src/tools/annotation/CobbAngleTool.ts +16 -1
  73. package/src/tools/annotation/EllipticalROITool.ts +16 -1
  74. package/src/tools/annotation/LengthTool.ts +16 -1
  75. package/src/tools/annotation/PlanarFreehandROITool.ts +12 -7
  76. package/src/tools/annotation/ProbeTool.ts +6 -1
  77. package/src/tools/annotation/RectangleROITool.ts +16 -1
  78. package/src/tools/base/AnnotationTool.ts +5 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cornerstonejs/tools",
3
- "version": "1.14.3",
3
+ "version": "1.15.0",
4
4
  "description": "Cornerstone3D Tools",
5
5
  "main": "dist/umd/index.js",
6
6
  "types": "dist/esm/index.d.ts",
@@ -29,7 +29,7 @@
29
29
  "webpack:watch": "webpack --mode development --progress --watch --config ./.webpack/webpack.dev.js"
30
30
  },
31
31
  "dependencies": {
32
- "@cornerstonejs/core": "^1.14.3",
32
+ "@cornerstonejs/core": "^1.15.0",
33
33
  "lodash.clonedeep": "4.5.0",
34
34
  "lodash.get": "^4.4.2"
35
35
  },
@@ -52,5 +52,5 @@
52
52
  "type": "individual",
53
53
  "url": "https://ohif.org/donate"
54
54
  },
55
- "gitHead": "a41ad226c52019627d584142eefc7b412022a3e8"
55
+ "gitHead": "d4478aa1242c48e10c95ef6dd511f2497e19b5e8"
56
56
  }
@@ -35,6 +35,7 @@ class ToolStyle {
35
35
  lineWidth: '1',
36
36
  lineDash: '',
37
37
  shadow: true,
38
+ textBoxVisibility: true,
38
39
  textBoxFontFamily: 'Helvetica Neue, Helvetica, Arial, sans-serif',
39
40
  textBoxFontSize: '14px',
40
41
  textBoxColor: 'rgb(255, 255, 0)',
@@ -205,6 +205,7 @@ class Synchronizer {
205
205
  }
206
206
 
207
207
  this._ignoreFiredEvents = true;
208
+ const promises = [];
208
209
  try {
209
210
  for (let i = 0; i < this._targetViewports.length; i++) {
210
211
  const targetViewport = this._targetViewports[i];
@@ -214,19 +215,26 @@ class Synchronizer {
214
215
  if (targetIsSource) {
215
216
  continue;
216
217
  }
217
-
218
- this._eventHandler(
219
- this,
220
- sourceViewport,
221
- targetViewport,
222
- sourceEvent,
223
- this._options
218
+ promises.push(
219
+ this._eventHandler(
220
+ this,
221
+ sourceViewport,
222
+ targetViewport,
223
+ sourceEvent,
224
+ this._options
225
+ )
224
226
  );
225
227
  }
226
228
  } catch (ex) {
227
229
  console.warn(`Synchronizer, for: ${this._eventName}`, ex);
228
230
  } finally {
229
- this._ignoreFiredEvents = false;
231
+ if (promises.length) {
232
+ Promise.allSettled(promises).then(() => {
233
+ this._ignoreFiredEvents = false;
234
+ });
235
+ } else {
236
+ this._ignoreFiredEvents = false;
237
+ }
230
238
  }
231
239
  }
232
240
 
@@ -1,4 +1,4 @@
1
- import { vec3 } from 'gl-matrix';
1
+ import { vec3, mat4 } from 'gl-matrix';
2
2
  import {
3
3
  getRenderingEngine,
4
4
  Types,
@@ -8,28 +8,27 @@ import {
8
8
  import { Synchronizer } from '../../store';
9
9
  import { jumpToSlice } from '../../utilities';
10
10
  import areViewportsCoplanar from './areViewportsCoplanar ';
11
+
12
+ const getSpatialRegistration = (targetId, sourceId) =>
13
+ utilities.spatialRegistrationMetadataProvider.get(
14
+ 'spatialRegistrationModule',
15
+ [targetId, sourceId]
16
+ );
17
+
11
18
  /**
12
19
  * Synchronizer callback to synchronize the source viewport image to the
13
- * target viewports closest image in its stack. There are two scenarios
20
+ * target viewports closest image in its stack.
14
21
  *
15
- * 1) viewports are in the same frameOfReferenceUID then we can use the
16
- * absolute imagePositionPatient for the source viewport's current image
17
- * and set the target viewport's image to the closest image in its stack
18
- * (which might have different slice thickness so cannot use slice number)
22
+ * This synchronizer does a setup (which can already be predefined as required)
23
+ * to register the target and soruce viewports. The registration will default
24
+ * to the identity registration if the same FOR is present in both viewports,
25
+ * unless the option `useInitialPosition` is set in the target viewport.
19
26
  *
20
- * 2) viewports have different frameOfReferenceUIDs then we look inside the
21
- * registrationMetadataProvider to check if there is a corresponding matrix
22
- * for mapping between the source and target viewport if so it is used to
23
- * and is applied to the imagePositionPatient of the source viewport's to
24
- * get the imagePositionPatient of the target viewport's closest image in
25
- * its stack.
26
- * Note for 2) The consuming apps using Cornerstone3D (OHIF, etc) are responsible
27
- * to provide such data in the registrationMetadataProvider. This can be done
27
+ * The consuming apps using Cornerstone3D (OHIF, etc) MAY provide such data in
28
+ * the registrationMetadataProvider to override the data here. This can be done
28
29
  * by various methods 1) Using spatialRegistrationModule inside dicom 2) assuming
29
30
  * the user has actually manually scrolled the target viewport to the correct
30
31
  * slice before initiating the synchronization 3) using some other method
31
- * But overall, the consuming app is responsible for providing the data.
32
- *
33
32
  *
34
33
  * @param synchronizerInstance - The Instance of the Synchronizer
35
34
  * @param sourceViewport - The list of IDs defining the source viewport.
@@ -53,13 +52,16 @@ export default async function stackImageSyncCallback(
53
52
  sourceViewport.viewportId
54
53
  ) as Types.IStackViewport;
55
54
 
55
+ const options = synchronizerInstance.getOptions(targetViewport.viewportId);
56
+
57
+ if (options?.disabled) {
58
+ return;
59
+ }
60
+
56
61
  const tViewport = renderingEngine.getViewport(
57
62
  targetViewport.viewportId
58
63
  ) as Types.IStackViewport;
59
64
 
60
- const frameOfReferenceUID1 = sViewport.getFrameOfReferenceUID();
61
- const frameOfReferenceUID2 = tViewport.getFrameOfReferenceUID();
62
-
63
65
  const imageId1 = sViewport.getCurrentImageId();
64
66
  const imagePlaneModule1 = metaData.get('imagePlaneModule', imageId1);
65
67
  const sourceImagePositionPatient = imagePlaneModule1.imagePositionPatient;
@@ -70,69 +72,62 @@ export default async function stackImageSyncCallback(
70
72
  return;
71
73
  }
72
74
 
73
- if (frameOfReferenceUID1 === frameOfReferenceUID2) {
74
- // if frames of references are the same we can use the absolute
75
- // imagePositionPatient to find the closest image in the target viewport's stack
76
- const closestImageIdIndex = _getClosestImageIdIndex(
77
- sourceImagePositionPatient,
78
- targetImageIds
79
- );
75
+ // if the frame of reference is different we need to use the registrationMetadataProvider
76
+ // and add that to the imagePositionPatient of the source viewport to get the
77
+ // imagePositionPatient of the target viewport's closest image in its stack
78
+ let registrationMatrixMat4 = getSpatialRegistration(
79
+ targetViewport.viewportId,
80
+ sourceViewport.viewportId
81
+ );
80
82
 
83
+ if (!registrationMatrixMat4) {
84
+ const frameOfReferenceUID1 = sViewport.getFrameOfReferenceUID();
85
+ const frameOfReferenceUID2 = tViewport.getFrameOfReferenceUID();
81
86
  if (
82
- closestImageIdIndex.index !== -1 &&
83
- tViewport.getCurrentImageIdIndex() !== closestImageIdIndex.index
87
+ frameOfReferenceUID1 === frameOfReferenceUID2 &&
88
+ options.useInitialPosition !== false
84
89
  ) {
85
- // await tViewport.setImageIdIndex(closestImageIdIndex.index);
86
- await jumpToSlice(tViewport.element, {
87
- imageIndex: closestImageIdIndex.index,
88
- });
89
-
90
- return;
91
- }
92
- } else {
93
- // if the frame of reference is different we need to use the registrationMetadataProvider
94
- // and add that to the imagePositionPatient of the source viewport to get the
95
- // imagePositionPatient of the target viewport's closest image in its stack
96
- const registrationMatrixMat4 =
97
- utilities.spatialRegistrationMetadataProvider.get(
98
- 'spatialRegistrationModule',
99
- [targetViewport.viewportId, sourceViewport.viewportId]
90
+ registrationMatrixMat4 = mat4.identity(mat4.create());
91
+ } else {
92
+ utilities.calculateViewportsSpatialRegistration(sViewport, tViewport);
93
+ registrationMatrixMat4 = getSpatialRegistration(
94
+ targetViewport.viewportId,
95
+ sourceViewport.viewportId
100
96
  );
101
-
97
+ }
102
98
  if (!registrationMatrixMat4) {
103
- throw new Error(
104
- `No registration matrix found for sourceViewport: ${sourceViewport.viewportId} and targetViewport: ${targetViewport.viewportId}, viewports with different frameOfReferenceUIDs must have a registration matrix in the registrationMetadataProvider. Use calculateViewportsRegistrationMatrix to calculate the matrix.`
105
- );
99
+ return;
106
100
  }
101
+ }
107
102
 
108
- // apply the registration matrix to the source viewport's imagePositionPatient
109
- // to get the target viewport's imagePositionPatient
110
- const targetImagePositionPatientWithRegistrationMatrix = vec3.transformMat4(
111
- vec3.create(),
112
- sourceImagePositionPatient,
113
- registrationMatrixMat4
114
- );
103
+ // apply the registration matrix to the source viewport's imagePositionPatient
104
+ // to get the target viewport's imagePositionPatient
105
+ const targetImagePositionPatientWithRegistrationMatrix = vec3.transformMat4(
106
+ vec3.create(),
107
+ sourceImagePositionPatient,
108
+ registrationMatrixMat4
109
+ );
115
110
 
116
- // find the closest image in the target viewport's stack to the
117
- // targetImagePositionPatientWithRegistrationMatrix
118
- const closestImageIdIndex2 = _getClosestImageIdIndex(
119
- targetImagePositionPatientWithRegistrationMatrix,
120
- targetImageIds
121
- );
111
+ // find the closest image in the target viewport's stack to the
112
+ // targetImagePositionPatientWithRegistrationMatrix
113
+ const closestImageIdIndex2 = _getClosestImageIdIndex(
114
+ targetImagePositionPatientWithRegistrationMatrix,
115
+ targetImageIds
116
+ );
122
117
 
123
- if (
124
- closestImageIdIndex2.index !== -1 &&
125
- tViewport.getCurrentImageIdIndex() !== closestImageIdIndex2.index
126
- ) {
127
- await jumpToSlice(tViewport.element, {
128
- imageIndex: closestImageIdIndex2.index,
129
- });
130
- }
118
+ if (
119
+ closestImageIdIndex2.index !== -1 &&
120
+ tViewport.getCurrentImageIdIndex() !== closestImageIdIndex2.index
121
+ ) {
122
+ await jumpToSlice(tViewport.element, {
123
+ imageIndex: closestImageIdIndex2.index,
124
+ });
131
125
  }
132
126
  }
133
127
 
134
128
  function _getClosestImageIdIndex(targetPoint, imageIds) {
135
129
  // todo: this does not assume orientation yet, but that can be added later
130
+ // todo: handle multiframe images
136
131
  return imageIds.reduce(
137
132
  (closestImageIdIndex, imageId, index) => {
138
133
  const { imagePositionPatient } = metaData.get(
@@ -39,7 +39,7 @@ export default function voiSyncCallback(
39
39
  voiRange: range,
40
40
  };
41
41
 
42
- if (options.syncInvertState && invertStateChanged) {
42
+ if (options?.syncInvertState && invertStateChanged) {
43
43
  tProperties.invert = invert;
44
44
  }
45
45
 
@@ -745,6 +745,21 @@ class AngleTool extends AnnotationTool {
745
745
  continue;
746
746
  }
747
747
 
748
+ const options = this.getLinkedTextBoxStyle(styleSpecifier, annotation);
749
+ if (!options.visibility) {
750
+ data.handles.textBox = {
751
+ hasMoved: false,
752
+ worldPosition: <Types.Point3>[0, 0, 0],
753
+ worldBoundingBox: {
754
+ topLeft: <Types.Point3>[0, 0, 0],
755
+ topRight: <Types.Point3>[0, 0, 0],
756
+ bottomLeft: <Types.Point3>[0, 0, 0],
757
+ bottomRight: <Types.Point3>[0, 0, 0],
758
+ },
759
+ };
760
+ continue;
761
+ }
762
+
748
763
  const textLines = this.configuration.getTextLines(data, targetId);
749
764
 
750
765
  if (!data.handles.textBox.hasMoved) {
@@ -768,7 +783,7 @@ class AngleTool extends AnnotationTool {
768
783
  textBoxPosition,
769
784
  canvasCoordinates,
770
785
  {},
771
- this.getLinkedTextBoxStyle(styleSpecifier, annotation)
786
+ options
772
787
  );
773
788
 
774
789
  const { x: left, y: top, width, height } = boundingBox;
@@ -776,6 +776,21 @@ class ArrowAnnotateTool extends AnnotationTool {
776
776
  continue;
777
777
  }
778
778
 
779
+ const options = this.getLinkedTextBoxStyle(styleSpecifier, annotation);
780
+ if (!options.visibility) {
781
+ data.handles.textBox = {
782
+ hasMoved: false,
783
+ worldPosition: <Types.Point3>[0, 0, 0],
784
+ worldBoundingBox: {
785
+ topLeft: <Types.Point3>[0, 0, 0],
786
+ topRight: <Types.Point3>[0, 0, 0],
787
+ bottomLeft: <Types.Point3>[0, 0, 0],
788
+ bottomRight: <Types.Point3>[0, 0, 0],
789
+ },
790
+ };
791
+ continue;
792
+ }
793
+
779
794
  // Need to update to sync w/ annotation while unlinked/not moved
780
795
  if (!data.handles.textBox.hasMoved) {
781
796
  // linked to the point that doesn't have the arrowhead by default
@@ -798,7 +813,7 @@ class ArrowAnnotateTool extends AnnotationTool {
798
813
  textBoxPosition,
799
814
  canvasCoordinates,
800
815
  {},
801
- this.getLinkedTextBoxStyle(styleSpecifier, annotation)
816
+ options
802
817
  );
803
818
 
804
819
  const { x: left, y: top, width, height } = boundingBox;
@@ -1162,11 +1162,26 @@ class BidirectionalTool extends AnnotationTool {
1162
1162
 
1163
1163
  renderStatus = true;
1164
1164
 
1165
- const textLines = this.configuration.getTextLines(data, targetId);
1165
+ const options = this.getLinkedTextBoxStyle(styleSpecifier, annotation);
1166
+ if (!options.visibility) {
1167
+ data.handles.textBox = {
1168
+ hasMoved: false,
1169
+ worldPosition: <Types.Point3>[0, 0, 0],
1170
+ worldBoundingBox: {
1171
+ topLeft: <Types.Point3>[0, 0, 0],
1172
+ topRight: <Types.Point3>[0, 0, 0],
1173
+ bottomLeft: <Types.Point3>[0, 0, 0],
1174
+ bottomRight: <Types.Point3>[0, 0, 0],
1175
+ },
1176
+ };
1177
+ continue;
1178
+ }
1166
1179
 
1180
+ const textLines = this.configuration.getTextLines(data, targetId);
1167
1181
  if (!textLines || textLines.length === 0) {
1168
1182
  continue;
1169
1183
  }
1184
+
1170
1185
  let canvasTextBoxCoords;
1171
1186
 
1172
1187
  if (!data.handles.textBox.hasMoved) {
@@ -1189,7 +1204,7 @@ class BidirectionalTool extends AnnotationTool {
1189
1204
  textBoxPosition,
1190
1205
  canvasCoordinates,
1191
1206
  {},
1192
- this.getLinkedTextBoxStyle(styleSpecifier, annotation)
1207
+ options
1193
1208
  );
1194
1209
 
1195
1210
  const { x: left, y: top, width, height } = boundingBox;
@@ -826,6 +826,21 @@ class CircleROITool extends AnnotationTool {
826
826
 
827
827
  renderStatus = true;
828
828
 
829
+ const options = this.getLinkedTextBoxStyle(styleSpecifier, annotation);
830
+ if (!options.visibility) {
831
+ data.handles.textBox = {
832
+ hasMoved: false,
833
+ worldPosition: <Types.Point3>[0, 0, 0],
834
+ worldBoundingBox: {
835
+ topLeft: <Types.Point3>[0, 0, 0],
836
+ topRight: <Types.Point3>[0, 0, 0],
837
+ bottomLeft: <Types.Point3>[0, 0, 0],
838
+ bottomRight: <Types.Point3>[0, 0, 0],
839
+ },
840
+ };
841
+ continue;
842
+ }
843
+
829
844
  const textLines = this.configuration.getTextLines(data, targetId);
830
845
  if (!textLines || textLines.length === 0) {
831
846
  continue;
@@ -854,7 +869,7 @@ class CircleROITool extends AnnotationTool {
854
869
  textBoxPosition,
855
870
  canvasCoordinates,
856
871
  {},
857
- this.getLinkedTextBoxStyle(styleSpecifier, annotation)
872
+ options
858
873
  );
859
874
 
860
875
  const { x: left, y: top, width, height } = boundingBox;
@@ -770,6 +770,21 @@ class CobbAngleTool extends AnnotationTool {
770
770
  continue;
771
771
  }
772
772
 
773
+ const options = this.getLinkedTextBoxStyle(styleSpecifier, annotation);
774
+ if (!options.visibility) {
775
+ data.handles.textBox = {
776
+ hasMoved: false,
777
+ worldPosition: <Types.Point3>[0, 0, 0],
778
+ worldBoundingBox: {
779
+ topLeft: <Types.Point3>[0, 0, 0],
780
+ topRight: <Types.Point3>[0, 0, 0],
781
+ bottomLeft: <Types.Point3>[0, 0, 0],
782
+ bottomRight: <Types.Point3>[0, 0, 0],
783
+ },
784
+ };
785
+ continue;
786
+ }
787
+
773
788
  const textLines = this.configuration.getTextLines(data, targetId);
774
789
 
775
790
  if (!data.handles.textBox.hasMoved) {
@@ -792,7 +807,7 @@ class CobbAngleTool extends AnnotationTool {
792
807
  textBoxPosition,
793
808
  canvasCoordinates,
794
809
  {},
795
- this.getLinkedTextBoxStyle(styleSpecifier, annotation)
810
+ options
796
811
  );
797
812
 
798
813
  const { x: left, y: top, width, height } = boundingBox;
@@ -950,6 +950,21 @@ class EllipticalROITool extends AnnotationTool {
950
950
 
951
951
  renderStatus = true;
952
952
 
953
+ const options = this.getLinkedTextBoxStyle(styleSpecifier, annotation);
954
+ if (!options.visibility) {
955
+ data.handles.textBox = {
956
+ hasMoved: false,
957
+ worldPosition: <Types.Point3>[0, 0, 0],
958
+ worldBoundingBox: {
959
+ topLeft: <Types.Point3>[0, 0, 0],
960
+ topRight: <Types.Point3>[0, 0, 0],
961
+ bottomLeft: <Types.Point3>[0, 0, 0],
962
+ bottomRight: <Types.Point3>[0, 0, 0],
963
+ },
964
+ };
965
+ continue;
966
+ }
967
+
953
968
  const textLines = this.configuration.getTextLines(data, targetId);
954
969
  if (!textLines || textLines.length === 0) {
955
970
  continue;
@@ -978,7 +993,7 @@ class EllipticalROITool extends AnnotationTool {
978
993
  textBoxPosition,
979
994
  canvasCoordinates,
980
995
  {},
981
- this.getLinkedTextBoxStyle(styleSpecifier, annotation)
996
+ options
982
997
  );
983
998
 
984
999
  const { x: left, y: top, width, height } = boundingBox;
@@ -732,6 +732,21 @@ class LengthTool extends AnnotationTool {
732
732
  return renderStatus;
733
733
  }
734
734
 
735
+ const options = this.getLinkedTextBoxStyle(styleSpecifier, annotation);
736
+ if (!options.visibility) {
737
+ data.handles.textBox = {
738
+ hasMoved: false,
739
+ worldPosition: <Types.Point3>[0, 0, 0],
740
+ worldBoundingBox: {
741
+ topLeft: <Types.Point3>[0, 0, 0],
742
+ topRight: <Types.Point3>[0, 0, 0],
743
+ bottomLeft: <Types.Point3>[0, 0, 0],
744
+ bottomRight: <Types.Point3>[0, 0, 0],
745
+ },
746
+ };
747
+ continue;
748
+ }
749
+
735
750
  const textLines = this.configuration.getTextLines(data, targetId);
736
751
 
737
752
  // Need to update to sync with annotation while unlinked/not moved
@@ -755,7 +770,7 @@ class LengthTool extends AnnotationTool {
755
770
  textBoxPosition,
756
771
  canvasCoordinates,
757
772
  {},
758
- this.getLinkedTextBoxStyle(styleSpecifier, annotation)
773
+ options
759
774
  );
760
775
 
761
776
  const { x: left, y: top, width, height } = boundingBox;
@@ -882,6 +882,17 @@ class PlanarFreehandROITool extends AnnotationTool {
882
882
  const data = annotation.data;
883
883
  const targetId = this.getTargetId(viewport);
884
884
 
885
+ const styleSpecifier: AnnotationStyle.StyleSpecifier = {
886
+ toolGroupId: this.toolGroupId,
887
+ toolName: this.getToolName(),
888
+ viewportId: enabledElement.viewport.id,
889
+ };
890
+
891
+ const options = this.getLinkedTextBoxStyle(styleSpecifier, annotation);
892
+ if (!options.visibility) {
893
+ return;
894
+ }
895
+
885
896
  const textLines = this.configuration.getTextLines(data, targetId);
886
897
  if (!textLines || textLines.length === 0) {
887
898
  return;
@@ -901,12 +912,6 @@ class PlanarFreehandROITool extends AnnotationTool {
901
912
  data.handles.textBox.worldPosition
902
913
  );
903
914
 
904
- const styleSpecifier: AnnotationStyle.StyleSpecifier = {
905
- toolGroupId: this.toolGroupId,
906
- toolName: this.getToolName(),
907
- viewportId: enabledElement.viewport.id,
908
- };
909
-
910
915
  const textBoxUID = '1';
911
916
  const boundingBox = drawLinkedTextBox(
912
917
  svgDrawingHelper,
@@ -916,7 +921,7 @@ class PlanarFreehandROITool extends AnnotationTool {
916
921
  textBoxPosition,
917
922
  canvasCoordinates,
918
923
  {},
919
- this.getLinkedTextBoxStyle(styleSpecifier, annotation)
924
+ options
920
925
  );
921
926
 
922
927
  const { x: left, y: top, width, height } = boundingBox;
@@ -527,6 +527,11 @@ class ProbeTool extends AnnotationTool {
527
527
 
528
528
  renderStatus = true;
529
529
 
530
+ const options = this.getLinkedTextBoxStyle(styleSpecifier, annotation);
531
+ if (!options.visibility) {
532
+ continue;
533
+ }
534
+
530
535
  const textLines = this.configuration.getTextLines(data, targetId);
531
536
  if (textLines) {
532
537
  const textCanvasCoordinates = [
@@ -541,7 +546,7 @@ class ProbeTool extends AnnotationTool {
541
546
  textUID,
542
547
  textLines,
543
548
  [textCanvasCoordinates[0], textCanvasCoordinates[1]],
544
- this.getLinkedTextBoxStyle(styleSpecifier, annotation)
549
+ options
545
550
  );
546
551
  }
547
552
  }
@@ -791,6 +791,21 @@ class RectangleROITool extends AnnotationTool {
791
791
 
792
792
  renderStatus = true;
793
793
 
794
+ const options = this.getLinkedTextBoxStyle(styleSpecifier, annotation);
795
+ if (!options.visibility) {
796
+ data.handles.textBox = {
797
+ hasMoved: false,
798
+ worldPosition: <Types.Point3>[0, 0, 0],
799
+ worldBoundingBox: {
800
+ topLeft: <Types.Point3>[0, 0, 0],
801
+ topRight: <Types.Point3>[0, 0, 0],
802
+ bottomLeft: <Types.Point3>[0, 0, 0],
803
+ bottomRight: <Types.Point3>[0, 0, 0],
804
+ },
805
+ };
806
+ continue;
807
+ }
808
+
794
809
  const textLines = this.configuration.getTextLines(data, targetId);
795
810
  if (!textLines || textLines.length === 0) {
796
811
  continue;
@@ -816,7 +831,7 @@ class RectangleROITool extends AnnotationTool {
816
831
  textBoxPosition,
817
832
  canvasCoordinates,
818
833
  {},
819
- this.getLinkedTextBoxStyle(styleSpecifier, annotation)
834
+ options
820
835
  );
821
836
 
822
837
  const { x: left, y: top, width, height } = boundingBox;
@@ -257,6 +257,11 @@ abstract class AnnotationTool extends AnnotationDisplayTool {
257
257
  // for the textBox.
258
258
 
259
259
  return {
260
+ visibility: this.getStyle(
261
+ 'textBoxVisibility',
262
+ specifications,
263
+ annotation
264
+ ),
260
265
  fontFamily: this.getStyle(
261
266
  'textBoxFontFamily',
262
267
  specifications,