Skip to content

Commit

Permalink
fix: Elliptical roi when in flipped/rotated state (#479)
Browse files Browse the repository at this point in the history
* better fix for elliptical roi

* type and tools.api

* save/restore compatible

* add flip h/flip v/ and rotation 90 buttons
  • Loading branch information
IbrahimCSAE authored Mar 22, 2023
1 parent 2c625b9 commit f0961ae
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 13 deletions.
1 change: 1 addition & 0 deletions common/reviews/api/tools.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1527,6 +1527,7 @@ interface EllipticalROIAnnotation extends Annotation {
};
label: string;
cachedStats?: ROICachedStats;
initialRotation: number;
};
}

Expand Down
20 changes: 14 additions & 6 deletions packages/adapters/src/adapters/Cornerstone3D/EllipticalROI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,19 +137,27 @@ class EllipticalROI {
static getTID300RepresentationArguments(tool, worldToImageCoords) {
const { data, finding, findingSites, metadata } = tool;
const { cachedStats = {}, handles } = data;

const rotation = data.initialRotation || 0;
const { referencedImageId } = metadata;

if (!referencedImageId) {
throw new Error(
"EllipticalROI.getTID300RepresentationArguments: referencedImageId is not defined"
);
}

const top = worldToImageCoords(referencedImageId, handles.points[0]);
const bottom = worldToImageCoords(referencedImageId, handles.points[1]);
const left = worldToImageCoords(referencedImageId, handles.points[2]);
const right = worldToImageCoords(referencedImageId, handles.points[3]);
let top, bottom, left, right;
// this way when it's restored we can assume the initial rotation is 0.
if (rotation == 90 || rotation == 270) {
bottom = worldToImageCoords(referencedImageId, handles.points[2]);
top = worldToImageCoords(referencedImageId, handles.points[3]);
left = worldToImageCoords(referencedImageId, handles.points[0]);
right = worldToImageCoords(referencedImageId, handles.points[1]);
} else {
top = worldToImageCoords(referencedImageId, handles.points[0]);
bottom = worldToImageCoords(referencedImageId, handles.points[1]);
left = worldToImageCoords(referencedImageId, handles.points[2]);
right = worldToImageCoords(referencedImageId, handles.points[3]);
}

// find the major axis and minor axis
const topBottomLength = Math.abs(top[1] - bottom[1]);
Expand Down
103 changes: 98 additions & 5 deletions packages/tools/examples/stackAnnotationTools/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import { RenderingEngine, Types, Enums } from '@cornerstonejs/core';
import {
RenderingEngine,
Types,
Enums,
getRenderingEngine,
} from '@cornerstonejs/core';
import {
initDemo,
createImageIdsAndCacheMetaData,
setTitleAndDescription,
addDropdownToToolbar,
addButtonToToolbar,
} from '../../../../utils/demo/helpers';
import * as cornerstoneTools from '@cornerstonejs/tools';

Expand All @@ -25,8 +31,10 @@ const {
Enums: csToolsEnums,
} = cornerstoneTools;

const { ViewportType } = Enums;
const { ViewportType, Events } = Enums;
const { MouseBindings } = csToolsEnums;
const renderingEngineId = 'myRenderingEngine';
const viewportId = 'CT_STACK';

// ======== Set up page ======== //
setTitleAndDescription(
Expand All @@ -46,10 +54,42 @@ element.style.height = '500px';

content.appendChild(element);

const info = document.createElement('div');
content.appendChild(info);

const instructions = document.createElement('p');
instructions.innerText = 'Left Click to use selected tool';
info.appendChild(instructions);

const rotationInfo = document.createElement('div');
info.appendChild(rotationInfo);

const flipHorizontalInfo = document.createElement('div');
info.appendChild(flipHorizontalInfo);

const flipVerticalInfo = document.createElement('div');
info.appendChild(flipVerticalInfo);

element.addEventListener(Events.CAMERA_MODIFIED, (_) => {
// Get the rendering engine
const renderingEngine = getRenderingEngine(renderingEngineId);

// Get the stack viewport
const viewport = <Types.IStackViewport>(
renderingEngine.getViewport(viewportId)
);

if (!viewport) {
return;
}

const { flipHorizontal, flipVertical } = viewport.getCamera();
const { rotation } = viewport.getProperties();

content.append(instructions);
rotationInfo.innerText = `Rotation: ${Math.round(rotation)}`;
flipHorizontalInfo.innerText = `Flip horizontal: ${flipHorizontal}`;
flipVerticalInfo.innerText = `Flip vertical: ${flipVertical}`;
});
// ============================= //

const toolGroupId = 'STACK_TOOL_GROUP_ID';
Expand Down Expand Up @@ -88,6 +128,61 @@ addDropdownToToolbar({
},
});

addButtonToToolbar({
title: 'Flip H',
onClick: () => {
// Get the rendering engine
const renderingEngine = getRenderingEngine(renderingEngineId);

// Get the stack viewport
const viewport = <Types.IStackViewport>(
renderingEngine.getViewport(viewportId)
);

const { flipHorizontal } = viewport.getCamera();
viewport.setCamera({ flipHorizontal: !flipHorizontal });

viewport.render();
},
});

addButtonToToolbar({
title: 'Flip V',
onClick: () => {
// Get the rendering engine
const renderingEngine = getRenderingEngine(renderingEngineId);

// Get the stack viewport
const viewport = <Types.IStackViewport>(
renderingEngine.getViewport(viewportId)
);

const { flipVertical } = viewport.getCamera();

viewport.setCamera({ flipVertical: !flipVertical });

viewport.render();
},
});

addButtonToToolbar({
title: 'Rotate Delta 90',
onClick: () => {
// Get the rendering engine
const renderingEngine = getRenderingEngine(renderingEngineId);

// Get the stack viewport
const viewport = <Types.IStackViewport>(
renderingEngine.getViewport(viewportId)
);

const { rotation } = viewport.getProperties();
viewport.setProperties({ rotation: rotation + 90 });

viewport.render();
},
});

/**
* Runs the demo
*/
Expand Down Expand Up @@ -148,11 +243,9 @@ async function run() {
});

// Instantiate a rendering engine
const renderingEngineId = 'myRenderingEngine';
const renderingEngine = new RenderingEngine(renderingEngineId);

// Create a stack viewport
const viewportId = 'CT_STACK';
const viewportInput = {
viewportId,
type: ViewportType.STACK,
Expand Down
20 changes: 18 additions & 2 deletions packages/tools/src/tools/annotation/EllipticalROITool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ class EllipticalROITool extends AnnotationTool {
activeHandleIndex: null,
},
cachedStats: {},
initialRotation: viewport.getRotation(),
},
};

Expand Down Expand Up @@ -765,9 +766,24 @@ class EllipticalROITool extends AnnotationTool {
const canvasCoordinates = points.map((p) =>
viewport.worldToCanvas(p)
) as [Types.Point2, Types.Point2, Types.Point2, Types.Point2];
const canvasCorners = <Array<Types.Point2>>(
getCanvasEllipseCorners(canvasCoordinates)

const rotation = Math.abs(
viewport.getRotation() - (data.initialRotation || 0)
);
let canvasCorners;

if (rotation == 90 || rotation == 270) {
canvasCorners = <Array<Types.Point2>>getCanvasEllipseCorners([
canvasCoordinates[2], // bottom
canvasCoordinates[3], // top
canvasCoordinates[0], // left
canvasCoordinates[1], // right
]);
} else {
canvasCorners = <Array<Types.Point2>>(
getCanvasEllipseCorners(canvasCoordinates) // bottom, top, left, right, keep as is
);
}

const { centerPointRadius } = this.configuration;

Expand Down
1 change: 1 addition & 0 deletions packages/tools/src/types/ToolSpecificAnnotationTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ export interface EllipticalROIAnnotation extends Annotation {
};
label: string;
cachedStats?: ROICachedStats;
initialRotation: number;
};
}

Expand Down

0 comments on commit f0961ae

Please sign in to comment.