@cornerstonejs/tools 1.70.6 → 1.70.7

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 (79) hide show
  1. package/dist/cjs/enums/Events.d.ts +2 -0
  2. package/dist/cjs/enums/Events.js +2 -0
  3. package/dist/cjs/enums/Events.js.map +1 -1
  4. package/dist/cjs/store/ToolGroupManager/ToolGroup.js +12 -0
  5. package/dist/cjs/store/ToolGroupManager/ToolGroup.js.map +1 -1
  6. package/dist/cjs/tools/OrientationMarkerTool.d.ts +0 -2
  7. package/dist/cjs/tools/OrientationMarkerTool.js +41 -27
  8. package/dist/cjs/tools/OrientationMarkerTool.js.map +1 -1
  9. package/dist/cjs/tools/annotation/BidirectionalTool.js +15 -8
  10. package/dist/cjs/tools/annotation/BidirectionalTool.js.map +1 -1
  11. package/dist/cjs/tools/annotation/CircleROITool.js +19 -18
  12. package/dist/cjs/tools/annotation/CircleROITool.js.map +1 -1
  13. package/dist/cjs/tools/annotation/EllipticalROITool.d.ts +1 -1
  14. package/dist/cjs/tools/annotation/EllipticalROITool.js +21 -20
  15. package/dist/cjs/tools/annotation/EllipticalROITool.js.map +1 -1
  16. package/dist/cjs/tools/annotation/PlanarFreehandROITool.js +27 -4
  17. package/dist/cjs/tools/annotation/PlanarFreehandROITool.js.map +1 -1
  18. package/dist/cjs/tools/annotation/RectangleROITool.js +18 -18
  19. package/dist/cjs/tools/annotation/RectangleROITool.js.map +1 -1
  20. package/dist/cjs/tools/annotation/SplineROITool.js +16 -2
  21. package/dist/cjs/tools/annotation/SplineROITool.js.map +1 -1
  22. package/dist/cjs/utilities/getCalibratedUnits.d.ts +1 -5
  23. package/dist/cjs/utilities/getCalibratedUnits.js +13 -45
  24. package/dist/cjs/utilities/getCalibratedUnits.js.map +1 -1
  25. package/dist/cjs/utilities/index.d.ts +2 -2
  26. package/dist/cjs/utilities/index.js +4 -4
  27. package/dist/cjs/utilities/index.js.map +1 -1
  28. package/dist/esm/enums/Events.js +2 -0
  29. package/dist/esm/enums/Events.js.map +1 -1
  30. package/dist/esm/store/ToolGroupManager/ToolGroup.js +12 -0
  31. package/dist/esm/store/ToolGroupManager/ToolGroup.js.map +1 -1
  32. package/dist/esm/tools/OrientationMarkerTool.js +42 -28
  33. package/dist/esm/tools/OrientationMarkerTool.js.map +1 -1
  34. package/dist/esm/tools/annotation/BidirectionalTool.js +16 -9
  35. package/dist/esm/tools/annotation/BidirectionalTool.js.map +1 -1
  36. package/dist/esm/tools/annotation/CircleROITool.js +21 -20
  37. package/dist/esm/tools/annotation/CircleROITool.js.map +1 -1
  38. package/dist/esm/tools/annotation/EllipticalROITool.js +23 -22
  39. package/dist/esm/tools/annotation/EllipticalROITool.js.map +1 -1
  40. package/dist/esm/tools/annotation/PlanarFreehandROITool.js +29 -6
  41. package/dist/esm/tools/annotation/PlanarFreehandROITool.js.map +1 -1
  42. package/dist/esm/tools/annotation/RectangleROITool.js +19 -19
  43. package/dist/esm/tools/annotation/RectangleROITool.js.map +1 -1
  44. package/dist/esm/tools/annotation/SplineROITool.js +17 -3
  45. package/dist/esm/tools/annotation/SplineROITool.js.map +1 -1
  46. package/dist/esm/utilities/getCalibratedUnits.js +13 -41
  47. package/dist/esm/utilities/getCalibratedUnits.js.map +1 -1
  48. package/dist/esm/utilities/index.js +2 -2
  49. package/dist/esm/utilities/index.js.map +1 -1
  50. package/dist/types/enums/Events.d.ts +2 -0
  51. package/dist/types/enums/Events.d.ts.map +1 -1
  52. package/dist/types/store/ToolGroupManager/ToolGroup.d.ts.map +1 -1
  53. package/dist/types/tools/OrientationMarkerTool.d.ts +0 -2
  54. package/dist/types/tools/OrientationMarkerTool.d.ts.map +1 -1
  55. package/dist/types/tools/annotation/BidirectionalTool.d.ts.map +1 -1
  56. package/dist/types/tools/annotation/CircleROITool.d.ts.map +1 -1
  57. package/dist/types/tools/annotation/EllipticalROITool.d.ts +1 -1
  58. package/dist/types/tools/annotation/EllipticalROITool.d.ts.map +1 -1
  59. package/dist/types/tools/annotation/PlanarFreehandROITool.d.ts.map +1 -1
  60. package/dist/types/tools/annotation/RectangleROITool.d.ts.map +1 -1
  61. package/dist/types/tools/annotation/SplineROITool.d.ts.map +1 -1
  62. package/dist/types/utilities/getCalibratedUnits.d.ts +1 -5
  63. package/dist/types/utilities/getCalibratedUnits.d.ts.map +1 -1
  64. package/dist/types/utilities/index.d.ts +2 -2
  65. package/dist/types/utilities/index.d.ts.map +1 -1
  66. package/dist/umd/index.js +1 -1
  67. package/dist/umd/index.js.map +1 -1
  68. package/package.json +3 -3
  69. package/src/enums/Events.ts +6 -0
  70. package/src/store/ToolGroupManager/ToolGroup.ts +16 -0
  71. package/src/tools/OrientationMarkerTool.ts +67 -49
  72. package/src/tools/annotation/BidirectionalTool.ts +28 -15
  73. package/src/tools/annotation/CircleROITool.ts +24 -25
  74. package/src/tools/annotation/EllipticalROITool.ts +28 -39
  75. package/src/tools/annotation/PlanarFreehandROITool.ts +56 -11
  76. package/src/tools/annotation/RectangleROITool.ts +23 -23
  77. package/src/tools/annotation/SplineROITool.ts +36 -4
  78. package/src/utilities/getCalibratedUnits.ts +18 -74
  79. package/src/utilities/index.ts +6 -6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cornerstonejs/tools",
3
- "version": "1.70.6",
3
+ "version": "1.70.7",
4
4
  "description": "Cornerstone3D Tools",
5
5
  "main": "src/index.ts",
6
6
  "types": "dist/types/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.70.6",
32
+ "@cornerstonejs/core": "^1.70.7",
33
33
  "@icr/polyseg-wasm": "0.4.0",
34
34
  "@types/offscreencanvas": "2019.7.3",
35
35
  "comlink": "^4.4.1",
@@ -59,5 +59,5 @@
59
59
  "type": "individual",
60
60
  "url": "https://ohif.org/donate"
61
61
  },
62
- "gitHead": "6358796a0031d69a9987ce089bbc27b8a3801dc1"
62
+ "gitHead": "4b6774d20ea66e2fa2815edc042b57565b816959"
63
63
  }
@@ -18,6 +18,12 @@ enum Events {
18
18
  */
19
19
  TOOL_ACTIVATED = 'CORNERSTONE_TOOLS_TOOL_ACTIVATED',
20
20
 
21
+ // fired when a viewport is added to the toolGroup
22
+ TOOLGROUP_VIEWPORT_ADDED = 'CORNERSTONE_TOOLS_TOOLGROUP_VIEWPORT_ADDED',
23
+
24
+ // fired when a viewport is removed from the toolGroup
25
+ TOOLGROUP_VIEWPORT_REMOVED = 'CORNERSTONE_TOOLS_TOOLGROUP_VIEWPORT_REMOVED',
26
+
21
27
  /**
22
28
  * Triggers on the eventTarget when a mode of a tool is changed (active, passive, enabled and disabled).
23
29
  *
@@ -239,6 +239,14 @@ export default class ToolGroup implements IToolGroup {
239
239
  if (runtimeSettings.get('useCursors')) {
240
240
  this.setViewportsCursorByToolName(toolName);
241
241
  }
242
+
243
+ const eventDetail = {
244
+ toolGroupId: this.id,
245
+ viewportId,
246
+ renderingEngineId: renderingEngineUIDToUse,
247
+ };
248
+
249
+ triggerEvent(eventTarget, Events.TOOLGROUP_VIEWPORT_ADDED, eventDetail);
242
250
  }
243
251
 
244
252
  /**
@@ -273,6 +281,14 @@ export default class ToolGroup implements IToolGroup {
273
281
  this.viewportsInfo.splice(indices[i], 1);
274
282
  }
275
283
  }
284
+
285
+ const eventDetail = {
286
+ toolGroupId: this.id,
287
+ viewportId,
288
+ renderingEngineId,
289
+ };
290
+
291
+ triggerEvent(eventTarget, Events.TOOLGROUP_VIEWPORT_REMOVED, eventDetail);
276
292
  }
277
293
 
278
294
  public setActiveStrategy(toolName: string, strategyName: string) {
@@ -9,11 +9,13 @@ import vtkPolyData from '@kitware/vtk.js/Common/DataModel/PolyData';
9
9
  import { BaseTool } from './base';
10
10
  import {
11
11
  Enums,
12
+ eventTarget,
12
13
  getEnabledElementByIds,
13
14
  getRenderingEngines,
14
15
  } from '@cornerstonejs/core';
15
16
  import { filterViewportsWithToolEnabled } from '../utilities/viewportFilters';
16
17
  import { getToolGroup } from '../store/ToolGroupManager';
18
+ import { Events } from '../enums';
17
19
 
18
20
  const OverlayMarkerType = {
19
21
  ANNOTATED_CUBE: 1,
@@ -36,8 +38,6 @@ class OrientationMarkerTool extends BaseTool {
36
38
 
37
39
  static OVERLAY_MARKER_TYPES = OverlayMarkerType;
38
40
 
39
- configuration_invalidated = true;
40
-
41
41
  constructor(
42
42
  toolProps = {},
43
43
  defaultToolProps = {
@@ -88,18 +88,16 @@ class OrientationMarkerTool extends BaseTool {
88
88
  ) {
89
89
  super(toolProps, defaultToolProps);
90
90
  this.orientationMarkers = {};
91
- this.configuration_invalidated = true;
92
91
  }
93
92
 
94
93
  onSetToolEnabled = (): void => {
95
94
  this.initViewports();
96
- this.configuration_invalidated = true;
97
95
  this._subscribeToViewportEvents();
98
96
  };
99
97
 
100
98
  onSetToolActive = (): void => {
101
99
  this.initViewports();
102
- this.configuration_invalidated = true;
100
+
103
101
  this._subscribeToViewportEvents();
104
102
  };
105
103
 
@@ -108,11 +106,6 @@ class OrientationMarkerTool extends BaseTool {
108
106
  this._unsubscribeToViewportNewVolumeSet();
109
107
  };
110
108
 
111
- reset = () => {
112
- this.configuration_invalidated = true;
113
- this.initViewports();
114
- };
115
-
116
109
  _getViewportsInfo = () => {
117
110
  const viewports = getToolGroup(this.toolGroupId).viewportsInfo;
118
111
 
@@ -130,50 +123,77 @@ class OrientationMarkerTool extends BaseTool {
130
123
  };
131
124
 
132
125
  _unsubscribeToViewportNewVolumeSet() {
133
- const viewportsInfo = this._getViewportsInfo();
134
- viewportsInfo.forEach(({ viewportId, renderingEngineId }) => {
135
- const { viewport } = getEnabledElementByIds(
136
- viewportId,
137
- renderingEngineId
138
- );
139
- const { element } = viewport;
140
-
141
- element.removeEventListener(
142
- Enums.Events.VOLUME_VIEWPORT_NEW_VOLUME,
143
- this.reset
144
- );
145
-
146
- // // // unsubscribe to element resize events
147
- const resizeObserver = this._resizeObservers.get(viewportId);
148
- resizeObserver.unobserve(element);
126
+ const unsubscribe = () => {
127
+ const viewportsInfo = this._getViewportsInfo();
128
+ viewportsInfo.forEach(({ viewportId, renderingEngineId }) => {
129
+ const { viewport } = getEnabledElementByIds(
130
+ viewportId,
131
+ renderingEngineId
132
+ );
133
+ const { element } = viewport;
134
+
135
+ element.removeEventListener(
136
+ Enums.Events.VOLUME_VIEWPORT_NEW_VOLUME,
137
+ this.initViewports.bind(this)
138
+ );
139
+
140
+ const resizeObserver = this._resizeObservers.get(viewportId);
141
+ resizeObserver.unobserve(element);
142
+ });
143
+ };
144
+
145
+ eventTarget.removeEventListener(Events.TOOLGROUP_VIEWPORT_ADDED, (evt) => {
146
+ if (evt.detail.toolGroupId !== this.toolGroupId) {
147
+ return;
148
+ }
149
+ unsubscribe();
150
+ this.initViewports();
149
151
  });
150
152
  }
151
153
 
152
154
  _subscribeToViewportEvents() {
153
- const viewportsInfo = this._getViewportsInfo();
154
-
155
- viewportsInfo.forEach(({ viewportId, renderingEngineId }) => {
156
- const { viewport } = getEnabledElementByIds(
157
- viewportId,
158
- renderingEngineId
159
- );
160
- const { element } = viewport;
161
-
162
- element.addEventListener(
163
- Enums.Events.VOLUME_VIEWPORT_NEW_VOLUME,
164
- this.reset
165
- );
166
-
167
- const resizeObserver = new ResizeObserver(() => {
168
- // Todo: i wish there was a better way to do this
169
- setTimeout(() => {
170
- this.reset();
171
- }, 100);
155
+ const subscribeToElementResize = () => {
156
+ const viewportsInfo = this._getViewportsInfo();
157
+ viewportsInfo.forEach(({ viewportId, renderingEngineId }) => {
158
+ const { viewport } = getEnabledElementByIds(
159
+ viewportId,
160
+ renderingEngineId
161
+ );
162
+ const { element } = viewport;
163
+ this.initViewports();
164
+
165
+ element.addEventListener(
166
+ Enums.Events.VOLUME_VIEWPORT_NEW_VOLUME,
167
+ this.initViewports.bind(this)
168
+ );
169
+
170
+ const resizeObserver = new ResizeObserver(() => {
171
+ // Todo: i wish there was a better way to do this
172
+ setTimeout(() => {
173
+ const { viewport } = getEnabledElementByIds(
174
+ viewportId,
175
+ renderingEngineId
176
+ );
177
+ this.resize(viewportId);
178
+ viewport.render();
179
+ }, 100);
180
+ });
181
+
182
+ resizeObserver.observe(element);
183
+
184
+ this._resizeObservers.set(viewportId, resizeObserver);
172
185
  });
186
+ };
173
187
 
174
- resizeObserver.observe(element);
188
+ subscribeToElementResize();
175
189
 
176
- this._resizeObservers.set(viewportId, resizeObserver);
190
+ eventTarget.addEventListener(Events.TOOLGROUP_VIEWPORT_ADDED, (evt) => {
191
+ if (evt.detail.toolGroupId !== this.toolGroupId) {
192
+ return;
193
+ }
194
+
195
+ subscribeToElementResize();
196
+ this.initViewports();
177
197
  });
178
198
  }
179
199
 
@@ -276,8 +296,6 @@ class OrientationMarkerTool extends BaseTool {
276
296
  viewport.addWidget(this.getToolName(), orientationWidget);
277
297
  renderWindow.render();
278
298
  viewport.getRenderingEngine().render();
279
-
280
- this.configuration_invalidated = false;
281
299
  }
282
300
 
283
301
  private async createCustomActor() {
@@ -2,10 +2,7 @@ import { vec2, vec3 } from 'gl-matrix';
2
2
  import { getEnabledElement, utilities as csUtils } from '@cornerstonejs/core';
3
3
  import type { Types } from '@cornerstonejs/core';
4
4
 
5
- import {
6
- getCalibratedLengthUnits,
7
- getCalibratedScale,
8
- } from '../../utilities/getCalibratedUnits';
5
+ import { getCalibratedLengthUnitsAndScale } from '../../utilities/getCalibratedUnits';
9
6
  import { roundNumber } from '../../utilities';
10
7
  import { AnnotationTool } from '../base';
11
8
  import throttle from '../../utilities/throttle';
@@ -40,7 +37,6 @@ import {
40
37
  TextBoxHandle,
41
38
  PublicToolProps,
42
39
  ToolProps,
43
- InteractionTypes,
44
40
  SVGDrawingHelper,
45
41
  } from '../../types';
46
42
  import { BidirectionalAnnotation } from '../../types/ToolSpecificAnnotationTypes';
@@ -1272,17 +1268,32 @@ class BidirectionalTool extends AnnotationTool {
1272
1268
  }
1273
1269
 
1274
1270
  const { imageData, dimensions } = image;
1275
- const scale = getCalibratedScale(image);
1276
- const dist1 = this._calculateLength(worldPos1, worldPos2) / scale;
1277
- const dist2 = this._calculateLength(worldPos3, worldPos4) / scale;
1278
- const length = dist1 > dist2 ? dist1 : dist2;
1279
- const width = dist1 > dist2 ? dist2 : dist1;
1280
-
1281
1271
  const index1 = transformWorldToIndex(imageData, worldPos1);
1282
1272
  const index2 = transformWorldToIndex(imageData, worldPos2);
1283
1273
  const index3 = transformWorldToIndex(imageData, worldPos3);
1284
1274
  const index4 = transformWorldToIndex(imageData, worldPos4);
1285
1275
 
1276
+ const handles1 = [index1, index2];
1277
+ const handles2 = [index3, index4];
1278
+
1279
+ const { scale: scale1, units: units1 } = getCalibratedLengthUnitsAndScale(
1280
+ image,
1281
+ handles1
1282
+ );
1283
+
1284
+ const { scale: scale2, units: units2 } = getCalibratedLengthUnitsAndScale(
1285
+ image,
1286
+ handles2
1287
+ );
1288
+
1289
+ const dist1 = this._calculateLength(worldPos1, worldPos2) / scale1;
1290
+ const dist2 = this._calculateLength(worldPos3, worldPos4) / scale2;
1291
+ const length = dist1 > dist2 ? dist1 : dist2;
1292
+ const width = dist1 > dist2 ? dist2 : dist1;
1293
+
1294
+ const lengthUnit = dist1 > dist2 ? units1 : units2;
1295
+ const widthUnit = dist1 > dist2 ? units2 : units1;
1296
+
1286
1297
  this._isInsideVolume(index1, index2, index3, index4, dimensions)
1287
1298
  ? (this.isHandleOutsideImage = false)
1288
1299
  : (this.isHandleOutsideImage = true);
@@ -1290,7 +1301,9 @@ class BidirectionalTool extends AnnotationTool {
1290
1301
  cachedStats[targetId] = {
1291
1302
  length,
1292
1303
  width,
1293
- unit: getCalibratedLengthUnits(null, image),
1304
+ unit: units1,
1305
+ lengthUnit,
1306
+ widthUnit,
1294
1307
  };
1295
1308
  }
1296
1309
 
@@ -1321,7 +1334,7 @@ class BidirectionalTool extends AnnotationTool {
1321
1334
 
1322
1335
  function defaultGetTextLines(data, targetId): string[] {
1323
1336
  const { cachedStats, label } = data;
1324
- const { length, width, unit } = cachedStats[targetId];
1337
+ const { length, width, unit, lengthUnit, widthUnit } = cachedStats[targetId];
1325
1338
 
1326
1339
  const textLines = [];
1327
1340
  if (label) {
@@ -1334,8 +1347,8 @@ function defaultGetTextLines(data, targetId): string[] {
1334
1347
  // spaceBetweenSlices & pixelSpacing &
1335
1348
  // magnitude in each direction? Otherwise, this is "px"?
1336
1349
  textLines.push(
1337
- `L: ${roundNumber(length)} ${unit}`,
1338
- `W: ${roundNumber(width)} ${unit}`
1350
+ `L: ${roundNumber(length)} ${lengthUnit || unit}`,
1351
+ `W: ${roundNumber(width)} ${widthUnit || unit}`
1339
1352
  );
1340
1353
 
1341
1354
  return textLines;
@@ -7,13 +7,8 @@ import {
7
7
  } from '@cornerstonejs/core';
8
8
  import type { Types } from '@cornerstonejs/core';
9
9
 
10
- import {
11
- getCalibratedLengthUnits,
12
- getCalibratedAreaUnits,
13
- getCalibratedScale,
14
- getCalibratedAspect,
15
- } from '../../utilities/getCalibratedUnits';
16
- import { roundNumber } from '../../utilities';
10
+ import { getCalibratedAspect } from '../../utilities/getCalibratedUnits';
11
+ import { getCalibratedLengthUnitsAndScale, roundNumber } from '../../utilities';
17
12
  import throttle from '../../utilities/throttle';
18
13
  import {
19
14
  addAnnotation,
@@ -891,30 +886,30 @@ class CircleROITool extends AnnotationTool {
891
886
 
892
887
  const { dimensions, imageData, metadata, hasPixelSpacing } = image;
893
888
 
894
- const worldPos1Index = transformWorldToIndex(imageData, worldPos1);
889
+ const pos1Index = transformWorldToIndex(imageData, worldPos1);
895
890
 
896
- worldPos1Index[0] = Math.floor(worldPos1Index[0]);
897
- worldPos1Index[1] = Math.floor(worldPos1Index[1]);
898
- worldPos1Index[2] = Math.floor(worldPos1Index[2]);
891
+ pos1Index[0] = Math.floor(pos1Index[0]);
892
+ pos1Index[1] = Math.floor(pos1Index[1]);
893
+ pos1Index[2] = Math.floor(pos1Index[2]);
899
894
 
900
- const worldPos2Index = transformWorldToIndex(imageData, worldPos2);
895
+ const pos2Index = transformWorldToIndex(imageData, worldPos2);
901
896
 
902
- worldPos2Index[0] = Math.floor(worldPos2Index[0]);
903
- worldPos2Index[1] = Math.floor(worldPos2Index[1]);
904
- worldPos2Index[2] = Math.floor(worldPos2Index[2]);
897
+ pos2Index[0] = Math.floor(pos2Index[0]);
898
+ pos2Index[1] = Math.floor(pos2Index[1]);
899
+ pos2Index[2] = Math.floor(pos2Index[2]);
905
900
 
906
901
  // Check if one of the indexes are inside the volume, this then gives us
907
902
  // Some area to do stats over.
908
903
 
909
- if (this._isInsideVolume(worldPos1Index, worldPos2Index, dimensions)) {
910
- const iMin = Math.min(worldPos1Index[0], worldPos2Index[0]);
911
- const iMax = Math.max(worldPos1Index[0], worldPos2Index[0]);
904
+ if (this._isInsideVolume(pos1Index, pos2Index, dimensions)) {
905
+ const iMin = Math.min(pos1Index[0], pos2Index[0]);
906
+ const iMax = Math.max(pos1Index[0], pos2Index[0]);
912
907
 
913
- const jMin = Math.min(worldPos1Index[1], worldPos2Index[1]);
914
- const jMax = Math.max(worldPos1Index[1], worldPos2Index[1]);
908
+ const jMin = Math.min(pos1Index[1], pos2Index[1]);
909
+ const jMax = Math.max(pos1Index[1], pos2Index[1]);
915
910
 
916
- const kMin = Math.min(worldPos1Index[2], worldPos2Index[2]);
917
- const kMax = Math.max(worldPos1Index[2], worldPos2Index[2]);
911
+ const kMin = Math.min(pos1Index[2], pos2Index[2]);
912
+ const kMax = Math.max(pos1Index[2], pos2Index[2]);
918
913
 
919
914
  const boundsIJK = [
920
915
  [iMin, iMax],
@@ -942,7 +937,11 @@ class CircleROITool extends AnnotationTool {
942
937
  worldPos2
943
938
  );
944
939
  const isEmptyArea = worldWidth === 0 && worldHeight === 0;
945
- const scale = getCalibratedScale(image);
940
+ const handles = [pos1Index, pos2Index];
941
+ const { scale, units, areaUnits } = getCalibratedLengthUnitsAndScale(
942
+ image,
943
+ handles
944
+ );
946
945
  const aspect = getCalibratedAspect(image);
947
946
  const area = Math.abs(
948
947
  Math.PI *
@@ -986,9 +985,9 @@ class CircleROITool extends AnnotationTool {
986
985
  statsArray: stats.array,
987
986
  pointsInShape: pointsInShape,
988
987
  isEmptyArea,
989
- areaUnit: getCalibratedAreaUnits(null, image),
988
+ areaUnit: areaUnits,
990
989
  radius: worldWidth / 2 / scale,
991
- radiusUnit: getCalibratedLengthUnits(null, image),
990
+ radiusUnit: units,
992
991
  perimeter: (2 * Math.PI * (worldWidth / 2)) / scale,
993
992
  modalityUnit,
994
993
  };
@@ -7,10 +7,7 @@ import {
7
7
  } from '@cornerstonejs/core';
8
8
  import type { Types } from '@cornerstonejs/core';
9
9
 
10
- import {
11
- getCalibratedAreaUnits,
12
- getCalibratedScale,
13
- } from '../../utilities/getCalibratedUnits';
10
+ import { getCalibratedLengthUnitsAndScale } from '../../utilities/getCalibratedUnits';
14
11
  import { roundNumber } from '../../utilities';
15
12
  import throttle from '../../utilities/throttle';
16
13
  import {
@@ -56,10 +53,7 @@ import { EllipticalROIAnnotation } from '../../types/ToolSpecificAnnotationTypes
56
53
  import triggerAnnotationRenderForViewportIds from '../../utilities/triggerAnnotationRenderForViewportIds';
57
54
  import { pointInShapeCallback } from '../../utilities/';
58
55
  import { StyleSpecifier } from '../../types/AnnotationStyle';
59
- import {
60
- ModalityUnitOptions,
61
- getModalityUnit,
62
- } from '../../utilities/getModalityUnit';
56
+ import { getModalityUnit } from '../../utilities/getModalityUnit';
63
57
  import { isViewportPreScaled } from '../../utilities/viewport/isViewportPreScaled';
64
58
  import { BasicStatsCalculator } from '../../utilities/math/basic';
65
59
 
@@ -791,12 +785,7 @@ class EllipticalROITool extends AnnotationTool {
791
785
  areaUnit: null,
792
786
  };
793
787
 
794
- this._calculateCachedStats(
795
- annotation,
796
- viewport,
797
- renderingEngine,
798
- enabledElement
799
- );
788
+ this._calculateCachedStats(annotation, viewport, renderingEngine);
800
789
  } else if (annotation.invalidated) {
801
790
  this._throttledCalculateCachedStats(
802
791
  annotation,
@@ -972,12 +961,7 @@ class EllipticalROITool extends AnnotationTool {
972
961
  return renderStatus;
973
962
  };
974
963
 
975
- _calculateCachedStats = (
976
- annotation,
977
- viewport,
978
- renderingEngine,
979
- enabledElement
980
- ) => {
964
+ _calculateCachedStats = (annotation, viewport, renderingEngine) => {
981
965
  const data = annotation.data;
982
966
  const { element } = viewport;
983
967
 
@@ -1010,34 +994,34 @@ class EllipticalROITool extends AnnotationTool {
1010
994
  continue;
1011
995
  }
1012
996
 
1013
- const { dimensions, imageData, metadata, hasPixelSpacing } = image;
997
+ const { dimensions, imageData, metadata } = image;
1014
998
 
1015
- const worldPos1Index = transformWorldToIndex(imageData, worldPos1);
999
+ const pos1Index = transformWorldToIndex(imageData, worldPos1);
1016
1000
 
1017
- worldPos1Index[0] = Math.floor(worldPos1Index[0]);
1018
- worldPos1Index[1] = Math.floor(worldPos1Index[1]);
1019
- worldPos1Index[2] = Math.floor(worldPos1Index[2]);
1001
+ pos1Index[0] = Math.floor(pos1Index[0]);
1002
+ pos1Index[1] = Math.floor(pos1Index[1]);
1003
+ pos1Index[2] = Math.floor(pos1Index[2]);
1020
1004
 
1021
- const worldPos2Index = transformWorldToIndex(imageData, worldPos2);
1005
+ const post2Index = transformWorldToIndex(imageData, worldPos2);
1022
1006
 
1023
- worldPos2Index[0] = Math.floor(worldPos2Index[0]);
1024
- worldPos2Index[1] = Math.floor(worldPos2Index[1]);
1025
- worldPos2Index[2] = Math.floor(worldPos2Index[2]);
1007
+ post2Index[0] = Math.floor(post2Index[0]);
1008
+ post2Index[1] = Math.floor(post2Index[1]);
1009
+ post2Index[2] = Math.floor(post2Index[2]);
1026
1010
 
1027
1011
  // Check if one of the indexes are inside the volume, this then gives us
1028
1012
  // Some area to do stats over.
1029
1013
 
1030
- if (this._isInsideVolume(worldPos1Index, worldPos2Index, dimensions)) {
1014
+ if (this._isInsideVolume(pos1Index, post2Index, dimensions)) {
1031
1015
  this.isHandleOutsideImage = false;
1032
1016
 
1033
- const iMin = Math.min(worldPos1Index[0], worldPos2Index[0]);
1034
- const iMax = Math.max(worldPos1Index[0], worldPos2Index[0]);
1017
+ const iMin = Math.min(pos1Index[0], post2Index[0]);
1018
+ const iMax = Math.max(pos1Index[0], post2Index[0]);
1035
1019
 
1036
- const jMin = Math.min(worldPos1Index[1], worldPos2Index[1]);
1037
- const jMax = Math.max(worldPos1Index[1], worldPos2Index[1]);
1020
+ const jMin = Math.min(pos1Index[1], post2Index[1]);
1021
+ const jMax = Math.max(pos1Index[1], post2Index[1]);
1038
1022
 
1039
- const kMin = Math.min(worldPos1Index[2], worldPos2Index[2]);
1040
- const kMax = Math.max(worldPos1Index[2], worldPos2Index[2]);
1023
+ const kMin = Math.min(pos1Index[2], post2Index[2]);
1024
+ const kMax = Math.max(pos1Index[2], post2Index[2]);
1041
1025
 
1042
1026
  const boundsIJK = [
1043
1027
  [iMin, iMax],
@@ -1065,7 +1049,13 @@ class EllipticalROITool extends AnnotationTool {
1065
1049
  worldPos2
1066
1050
  );
1067
1051
  const isEmptyArea = worldWidth === 0 && worldHeight === 0;
1068
- const scale = getCalibratedScale(image);
1052
+
1053
+ const handles = [pos1Index, post2Index];
1054
+ const { scale, areaUnits } = getCalibratedLengthUnitsAndScale(
1055
+ image,
1056
+ handles
1057
+ );
1058
+
1069
1059
  const area =
1070
1060
  Math.abs(Math.PI * (worldWidth / 2) * (worldHeight / 2)) /
1071
1061
  scale /
@@ -1095,7 +1085,6 @@ class EllipticalROITool extends AnnotationTool {
1095
1085
  );
1096
1086
 
1097
1087
  const stats = this.configuration.statsCalculator.getStatistics();
1098
-
1099
1088
  cachedStats[targetId] = {
1100
1089
  Modality: metadata.Modality,
1101
1090
  area,
@@ -1105,7 +1094,7 @@ class EllipticalROITool extends AnnotationTool {
1105
1094
  statsArray: stats.array,
1106
1095
  pointsInShape,
1107
1096
  isEmptyArea,
1108
- areaUnit: getCalibratedAreaUnits(null, image),
1097
+ areaUnit: areaUnits,
1109
1098
  modalityUnit,
1110
1099
  };
1111
1100
  } else {
@@ -7,11 +7,8 @@ import {
7
7
  import type { Types } from '@cornerstonejs/core';
8
8
  import { vec3 } from 'gl-matrix';
9
9
 
10
- import {
11
- getCalibratedAreaUnits,
12
- getCalibratedScale,
13
- } from '../../utilities/getCalibratedUnits';
14
- import { roundNumber } from '../../utilities';
10
+ import { getCalibratedLengthUnitsAndScale } from '../../utilities/getCalibratedUnits';
11
+ import { math, roundNumber } from '../../utilities';
15
12
  import { polyline } from '../../utilities/math';
16
13
  import { filterAnnotationsForDisplay } from '../../utilities/planar';
17
14
  import throttle from '../../utilities/throttle';
@@ -727,11 +724,6 @@ class PlanarFreehandROITool extends ContourSegmentationBaseTool {
727
724
  const deltaInX = vec3.distance(originalWorldPoint, deltaXPoint);
728
725
  const deltaInY = vec3.distance(originalWorldPoint, deltaYPoint);
729
726
 
730
- const scale = getCalibratedScale(image);
731
- let area = polyline.getArea(canvasCoordinates) / scale / scale;
732
- // Convert from canvas_pixels ^2 to mm^2
733
- area *= deltaInX * deltaInY;
734
-
735
727
  const worldPosIndex = csUtils.transformWorldToIndex(imageData, points[0]);
736
728
  worldPosIndex[0] = Math.floor(worldPosIndex[0]);
737
729
  worldPosIndex[1] = Math.floor(worldPosIndex[1]);
@@ -764,6 +756,59 @@ class PlanarFreehandROITool extends ContourSegmentationBaseTool {
764
756
  kMax = Math.max(kMax, worldPosIndex[2]);
765
757
  }
766
758
 
759
+ const worldPosIndex2 = csUtils.transformWorldToIndex(
760
+ imageData,
761
+ points[1]
762
+ );
763
+ worldPosIndex2[0] = Math.floor(worldPosIndex2[0]);
764
+ worldPosIndex2[1] = Math.floor(worldPosIndex2[1]);
765
+ worldPosIndex2[2] = Math.floor(worldPosIndex2[2]);
766
+
767
+ const { scale, areaUnits } = getCalibratedLengthUnitsAndScale(
768
+ image,
769
+ () => {
770
+ const polyline = data.contour.polyline;
771
+ const numPoints = polyline.length;
772
+ const projectedPolyline = new Array(numPoints);
773
+
774
+ for (let i = 0; i < numPoints; i++) {
775
+ projectedPolyline[i] = viewport.worldToCanvas(polyline[i]);
776
+ }
777
+
778
+ const {
779
+ maxX: canvasMaxX,
780
+ maxY: canvasMaxY,
781
+ minX: canvasMinX,
782
+ minY: canvasMinY,
783
+ } = math.polyline.getAABB(projectedPolyline);
784
+
785
+ const topLeftBBWorld = viewport.canvasToWorld([
786
+ canvasMinX,
787
+ canvasMinY,
788
+ ]);
789
+
790
+ const topLeftBBIndex = csUtils.transformWorldToIndex(
791
+ imageData,
792
+ topLeftBBWorld
793
+ );
794
+
795
+ const bottomRightBBWorld = viewport.canvasToWorld([
796
+ canvasMaxX,
797
+ canvasMaxY,
798
+ ]);
799
+
800
+ const bottomRightBBIndex = csUtils.transformWorldToIndex(
801
+ imageData,
802
+ bottomRightBBWorld
803
+ );
804
+
805
+ return [topLeftBBIndex, bottomRightBBIndex];
806
+ }
807
+ );
808
+ let area = polyline.getArea(canvasCoordinates) / scale / scale;
809
+ // Convert from canvas_pixels ^2 to mm^2
810
+ area *= deltaInX * deltaInY;
811
+
767
812
  // Expand bounding box
768
813
  const iDelta = 0.01 * (iMax - iMin);
769
814
  const jDelta = 0.01 * (jMax - jMin);
@@ -852,7 +897,7 @@ class PlanarFreehandROITool extends ContourSegmentationBaseTool {
852
897
  stdDev: stats.stdDev?.value,
853
898
  statsArray: stats.array,
854
899
  pointsInShape: pointsInShape,
855
- areaUnit: getCalibratedAreaUnits(null, image),
900
+ areaUnit: areaUnits,
856
901
  modalityUnit,
857
902
  };
858
903
  }