diff --git a/packages/tools/src/tools/annotation/RectangleROITool.ts b/packages/tools/src/tools/annotation/RectangleROITool.ts index a3cfc0c67e..fb63b5f2da 100644 --- a/packages/tools/src/tools/annotation/RectangleROITool.ts +++ b/packages/tools/src/tools/annotation/RectangleROITool.ts @@ -53,7 +53,7 @@ import { isViewportPreScaled } from '../../utilities/viewport/isViewportPreScale import { BasicStatsCalculator } from '../../utilities/math/basic'; import { getStyleProperty } from '../../stateManagement/annotation/config/helpers'; -const { transformWorldToIndex } = csUtils; +const { transformWorldToIndex, transformWorldToIndexContinuous } = csUtils; /** * RectangleROIAnnotation let you draw annotations that measures the statistics @@ -841,11 +841,11 @@ class RectangleROITool extends AnnotationTool { const { dimensions, imageData, metadata, voxelManager } = image; - const indexHandles = worldHandles.map((worldHandle) => - transformWorldToIndex(imageData, worldHandle) + const continuousIndexHandles = worldHandles.map((worldHandle) => + transformWorldToIndexContinuous(imageData, worldHandle) ); - const pos1Index = indexHandles[0].map(Math.floor); - const pos2Index = indexHandles[3].map(Math.floor); + const pos1Index = transformWorldToIndex(imageData, worldHandles[0]); + const pos2Index = transformWorldToIndex(imageData, worldHandles[3]); // Check if one of the indexes are inside the volume, this then gives us // Some area to do stats over. @@ -874,12 +874,12 @@ class RectangleROITool extends AnnotationTool { const calibrate = getCalibratedLengthUnitsAndScale(image, handles); const width = RectangleROITool.calculateLengthInIndex(calibrate, [ - indexHandles[0], - indexHandles[1], + continuousIndexHandles[0], + continuousIndexHandles[1], ]); const height = RectangleROITool.calculateLengthInIndex(calibrate, [ - indexHandles[0], - indexHandles[2], + continuousIndexHandles[0], + continuousIndexHandles[2], ]); const area = Math.abs(width * height); const { areaUnit } = calibrate; diff --git a/packages/tools/src/tools/annotation/UltrasoundDirectionalTool.ts b/packages/tools/src/tools/annotation/UltrasoundDirectionalTool.ts index 0acb38203b..2832574c3e 100644 --- a/packages/tools/src/tools/annotation/UltrasoundDirectionalTool.ts +++ b/packages/tools/src/tools/annotation/UltrasoundDirectionalTool.ts @@ -47,7 +47,7 @@ import type { import type { StyleSpecifier } from '../../types/AnnotationStyle'; import { getCalibratedProbeUnitsAndValue } from '../../utilities/getCalibratedUnits'; import { lineSegment } from '../../utilities/math'; -const { transformWorldToIndex } = csUtils; +const { transformWorldToIndexContinuous } = csUtils; /** * The `UltrasoundDirectionalTool` class is a tool for creating directional ultrasound annotations. @@ -767,8 +767,8 @@ class UltrasoundDirectionalTool extends AnnotationTool { const worldPos1 = data.handles.points[0]; const worldPos2 = data.handles.points[1]; - const imageIndex1 = transformWorldToIndex(imageData, worldPos1); - const imageIndex2 = transformWorldToIndex(imageData, worldPos2); + const imageIndex1 = transformWorldToIndexContinuous(imageData, worldPos1); + const imageIndex2 = transformWorldToIndexContinuous(imageData, worldPos2); const { values: values1, units: units1 } = getCalibratedProbeUnitsAndValue(image, [imageIndex1]); diff --git a/packages/tools/test/RectangleROIArea.jest.js b/packages/tools/test/RectangleROIArea.jest.js new file mode 100644 index 0000000000..4e7dbe9d70 --- /dev/null +++ b/packages/tools/test/RectangleROIArea.jest.js @@ -0,0 +1,63 @@ +import RectangleROITool from '../src/tools/annotation/RectangleROITool'; + +describe('Rectangle ROI area', () => { + it('uses continuous indices for physical area', () => { + const tool = new RectangleROITool(); + const targetId = 'imageId:test'; + const forEach = jest.fn(); + const image = { + dimensions: [100, 100, 1], + hasPixelSpacing: true, + imageData: { + worldToIndex: ([x, y, z]) => [x / 5, y / 5, z], + }, + metadata: { Modality: 'CT' }, + spacing: [5, 5, 1], + voxelManager: { forEach }, + }; + const annotation = { + data: { + cachedStats: { [targetId]: {} }, + handles: { + points: [ + [2, 2, 0], + [29, 2, 0], + [2, 18, 0], + [29, 18, 0], + ], + }, + }, + invalidated: false, + metadata: {}, + }; + const viewport = { + element: document.createElement('div'), + }; + + tool.getTargetImageData = () => image; + tool.configuration.statsCalculator = { + getStatistics: () => ({ array: [] }), + statsCallback: jest.fn(), + }; + + tool._calculateCachedStats( + annotation, + [0, 0, 1], + [0, 1, 0], + {}, + { viewport } + ); + + expect(annotation.data.cachedStats[targetId].area).toBeCloseTo(27 * 16); + expect(forEach).toHaveBeenCalledWith( + tool.configuration.statsCalculator.statsCallback, + expect.objectContaining({ + boundsIJK: [ + [0, 6], + [0, 4], + [0, 0], + ], + }) + ); + }); +}); diff --git a/packages/tools/test/UltrasoundDirectionalTool.jest.js b/packages/tools/test/UltrasoundDirectionalTool.jest.js new file mode 100644 index 0000000000..5205313724 --- /dev/null +++ b/packages/tools/test/UltrasoundDirectionalTool.jest.js @@ -0,0 +1,59 @@ +import UltrasoundDirectionalTool from '../src/tools/annotation/UltrasoundDirectionalTool'; + +describe('Ultrasound Directional measurements', () => { + it('uses continuous indices for calibrated endpoint values', () => { + const tool = new UltrasoundDirectionalTool(); + const targetId = 'imageId:test'; + const image = { + calibration: { + sequenceOfUltrasoundRegions: [ + { + regionDataType: 1, + regionLocationMinX0: 0, + regionLocationMaxX1: 100, + regionLocationMinY0: 0, + regionLocationMaxY1: 100, + physicalUnitsXDirection: 4, + physicalUnitsYDirection: 7, + physicalDeltaX: 2, + physicalDeltaY: 3, + }, + ], + }, + imageData: { + worldToIndex: ([x, y, z]) => [x, y, z], + }, + }; + const annotation = { + data: { + cachedStats: { [targetId]: {} }, + handles: { + points: [ + [0.2, 0.3, 0], + [10.7, 4.8, 0], + ], + }, + }, + invalidated: false, + }; + const viewport = { + element: document.createElement('div'), + worldToCanvas: ([x, y]) => [x, y], + }; + + tool.getTargetImageData = () => image; + + tool._calculateCachedStats(annotation, {}, { viewport }); + + const { xValues, yValues, isHorizontal, units, isUnitless } = + annotation.data.cachedStats[targetId]; + + expect(xValues[0]).toBeCloseTo(0.4); + expect(xValues[1]).toBeCloseTo(21.4); + expect(yValues[0]).toBeCloseTo(0.9); + expect(yValues[1]).toBeCloseTo(14.4); + expect(isHorizontal).toBe(true); + expect(units).toEqual(['seconds', 'cm/sec']); + expect(isUnitless).toBe(false); + }); +});