diff --git a/src/tools/annotation/AngleTool.js b/src/tools/annotation/AngleTool.js index bfdce7cf1..977f72bf8 100644 --- a/src/tools/annotation/AngleTool.js +++ b/src/tools/annotation/AngleTool.js @@ -12,14 +12,14 @@ import toolColors from './../../stateManagement/toolColors.js'; import { moveNewHandle } from './../../manipulators/index.js'; // Drawing import { - getNewContext, draw, - setShadow, + drawHandles, drawJoinedLines, + drawLinkedTextBox, + getNewContext, + setShadow, } from './../../drawing/index.js'; -import drawLinkedTextBox from './../../drawing/drawLinkedTextBox.js'; import { textBoxWidth } from './../../drawing/drawTextBox.js'; -import drawHandles from './../../drawing/drawHandles.js'; import lineSegDistance from './../../util/lineSegDistance.js'; import roundToDecimal from './../../util/roundToDecimal.js'; import { angleCursor } from '../cursors/index.js'; @@ -29,6 +29,9 @@ import getPixelSpacing from '../../util/getPixelSpacing'; import throttle from '../../util/throttle'; import { getModule } from '../../store/index'; +/** Private symbols */ +const _angleAnchorSet = Symbol('angleAnchorSet'); + /** * @public * @class AngleTool @@ -95,6 +98,7 @@ export default class AngleTool extends BaseAnnotationTool { hasBoundingBox: true, }, }, + [_angleAnchorSet]: false, }; } @@ -162,9 +166,7 @@ export default class AngleTool extends BaseAnnotationTool { const lineWidth = toolStyle.getToolWidth(); - for (let i = 0; i < toolData.data.length; i++) { - const data = toolData.data[i]; - + for (const data of toolData.data) { if (data.visible === false) { continue; } @@ -219,7 +221,12 @@ export default class AngleTool extends BaseAnnotationTool { } } - if (data.rAngle) { + const shouldRenderTextBox = + data[_angleAnchorSet] && + !Number.isNaN(data.rAngle) && + typeof data.rAngle === 'number'; + + if (shouldRenderTextBox) { const text = textBoxText(data, rowPixelSpacing, colPixelSpacing); const distance = 15; @@ -320,6 +327,7 @@ export default class AngleTool extends BaseAnnotationTool { } measurementData.handles.end.active = true; + measurementData[_angleAnchorSet] = true; // Middle (anchor) set. external.cornerstone.updateImage(element); diff --git a/src/tools/annotation/AngleTool.test.js b/src/tools/annotation/AngleTool.test.js index 1f5e10b5e..488f38309 100644 --- a/src/tools/annotation/AngleTool.test.js +++ b/src/tools/annotation/AngleTool.test.js @@ -1,19 +1,49 @@ import AngleTool from './AngleTool.js'; -import { getToolState } from './../../stateManagement/toolState.js'; +import { + addToolState, + getToolState, +} from './../../stateManagement/toolState.js'; +import { draw, drawLinkedTextBox } from './../../drawing/index.js'; +import { moveNewHandle } from './../../manipulators/index.js'; +import * as drawTextBoxModule from './../../drawing/drawTextBox.js'; +import * as getPixelSpacing from '../../util/getPixelSpacing'; jest.mock('./../../stateManagement/toolState.js', () => ({ + addToolState: jest.fn(), getToolState: jest.fn(), })); +jest.mock('./../../drawing/index.js', () => ({ + draw: jest.fn(), + drawHandles: jest.fn(), + drawJoinedLines: jest.fn(), + drawLinkedTextBox: jest.fn(), + getNewContext: jest.fn(() => ({})), + setShadow: jest.fn(), +})); + +jest.mock('./../../manipulators/index.js', () => ({ + moveNewHandle: jest.fn(), +})); + jest.mock('./../../importInternal.js', () => ({ default: jest.fn(), })); jest.mock('./../../externalModules.js', () => ({ cornerstone: { + invalidate: jest.fn(), + internal: { + getTransform: jest.fn(() => ({ + invert: jest.fn(), + transformPoint: jest.fn((x, y) => ({ x, y })), + })), + }, metaData: { get: jest.fn(), }, + pixelToCanvas: () => ({ x: 100, y: 100 }), + updateImage: jest.fn(), }, })); @@ -32,6 +62,10 @@ const image = { }; describe('AngleTool.js', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + describe('default values', () => { it('has a default name of "Angle"', () => { const defaultName = 'Angle'; @@ -169,5 +203,93 @@ describe('AngleTool.js', () => { expect(renderResult).toBe(undefined); }); + + it('should not render linked text box if angle anchor is unset', () => { + const instantiatedTool = new AngleTool('AngleTool'); + + jest + .spyOn(getPixelSpacing, 'default') + .mockReturnValue({ rowPixelSpacing: 1, columnPixelSpacing: 1 }); + + instantiatedTool.addNewMeasurement({ + detail: goodMouseEventData, + preventDefault: jest.fn(), + stopPropagation: jest.fn(), + }); + + const addedMeasurementData = addToolState.mock.calls[0][2]; + + addedMeasurementData.rAngle = 0; + getToolState.mockReturnValueOnce({ + data: [addedMeasurementData], + }); + + const mockEvent = { + currentTarget: {}, + detail: { + enabledElement: undefined, + canvasContext: { canvas: { width: 100, height: 100 } }, + element: {}, + }, + }; + + instantiatedTool.renderToolData(mockEvent); + + const drawCallback = draw.mock.calls[0][1]; + + drawCallback({}); + + expect(drawLinkedTextBox).not.toHaveBeenCalled(); + }); + + it('should render linked text box if angle anchor is set', () => { + const instantiatedTool = new AngleTool('AngleTool'); + + jest + .spyOn(getPixelSpacing, 'default') + .mockReturnValue({ rowPixelSpacing: 1, columnPixelSpacing: 1 }); + jest.spyOn(drawTextBoxModule, 'textBoxWidth').mockReturnValue(100); + + instantiatedTool.addNewMeasurement({ + detail: goodMouseEventData, + preventDefault: jest.fn(), + stopPropagation: jest.fn(), + }); + + const moveNewHandleCallback = moveNewHandle.mock.calls[0][6]; + + moveNewHandleCallback(true); + + const addedMeasurementData = addToolState.mock.calls[0][2]; + + // Sets mock angle value + addedMeasurementData.handles.start.x = 0; + addedMeasurementData.handles.start.y = 50; + addedMeasurementData.handles.middle.x = 0; + addedMeasurementData.handles.middle.y = 0; + addedMeasurementData.handles.end.x = 50; + addedMeasurementData.handles.end.y = 20; + + getToolState.mockReturnValueOnce({ + data: [addedMeasurementData], + }); + + const mockEvent = { + currentTarget: {}, + detail: { + enabledElement: undefined, + canvasContext: { canvas: { width: 100, height: 100 } }, + element: {}, + }, + }; + + instantiatedTool.renderToolData(mockEvent); + + const drawCallback = draw.mock.calls[0][1]; + + drawCallback({}); + + expect(drawLinkedTextBox).toHaveBeenCalledTimes(1); + }); }); });