Skip to content

Commit

Permalink
feat: Add AngleTool and MagnifyTool (#97)
Browse files Browse the repository at this point in the history
* fix: getCachedImage for imageURI

* feat: Add angleTool

* feat: add magnify tool

* add magnify example

* apply review comments
  • Loading branch information
sedghi authored May 10, 2022
1 parent 1aa2fec commit 2c4c800
Show file tree
Hide file tree
Showing 12 changed files with 1,369 additions and 7 deletions.
119 changes: 118 additions & 1 deletion common/reviews/api/tools.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,89 @@ function addSegmentations(segmentationInputArray: SegmentationPublicInput[]): vo
// @public (undocumented)
export function addTool(ToolClass: any): void;

// @public (undocumented)
interface AngleAnnotation extends Annotation {
// (undocumented)
data: {
handles: {
points: Types_2.Point3[];
activeHandleIndex: number | null;
textBox: {
hasMoved: boolean;
worldPosition: Types_2.Point3;
worldBoundingBox: {
topLeft: Types_2.Point3;
topRight: Types_2.Point3;
bottomLeft: Types_2.Point3;
bottomRight: Types_2.Point3;
};
};
};
label: string;
cachedStats: {
[targetId: string]: {
angle: number;
};
};
};
}

// @public (undocumented)
export class AngleTool extends AnnotationTool {
constructor(toolProps?: PublicToolProps, defaultToolProps?: ToolProps);
// (undocumented)
_activateDraw: (element: HTMLDivElement) => void;
// (undocumented)
_activateModify: (element: HTMLDivElement) => void;
// (undocumented)
addNewAnnotation: (evt: EventTypes_2.MouseDownActivateEventType) => AngleAnnotation;
// (undocumented)
angleStartedNotYetCompleted: boolean;
// (undocumented)
_calculateCachedStats(annotation: any, renderingEngine: any, enabledElement: any): any;
// (undocumented)
cancel: (element: HTMLDivElement) => any;
// (undocumented)
_deactivateDraw: (element: HTMLDivElement) => void;
// (undocumented)
_deactivateModify: (element: HTMLDivElement) => void;
// (undocumented)
editData: {
annotation: any;
viewportIdsToRender: string[];
handleIndex?: number;
movingTextBox?: boolean;
newAnnotation?: boolean;
hasMoved?: boolean;
} | null;
// (undocumented)
_getTextLines(data: any, targetId: any): string[];
// (undocumented)
handleSelectedCallback(evt: EventTypes_2.MouseDownEventType, annotation: AngleAnnotation, handle: ToolHandle, interactionType?: string): void;
// (undocumented)
isDrawing: boolean;
// (undocumented)
isHandleOutsideImage: boolean;
// (undocumented)
isPointNearTool: (element: HTMLDivElement, annotation: AngleAnnotation, canvasCoords: Types_2.Point2, proximity: number) => boolean;
// (undocumented)
mouseDragCallback: any;
// (undocumented)
_mouseDragCallback: (evt: EventTypes_2.MouseDragEventType | EventTypes_2.MouseMoveEventType) => void;
// (undocumented)
_mouseUpCallback: (evt: EventTypes_2.MouseUpEventType | EventTypes_2.MouseClickEventType) => void;
// (undocumented)
renderAnnotation: (enabledElement: Types_2.IEnabledElement, svgDrawingHelper: any) => void;
// (undocumented)
_throttledCalculateCachedStats: any;
// (undocumented)
static toolName: string;
// (undocumented)
toolSelectedCallback: (evt: EventTypes_2.MouseDownEventType, annotation: AngleAnnotation, interactionType: InteractionTypes) => void;
// (undocumented)
touchDragCallback: any;
}

// @public (undocumented)
type Annotation = {
annotationUID?: string;
Expand Down Expand Up @@ -2378,6 +2461,39 @@ declare namespace locking {
}
}

// @public (undocumented)
export class MagnifyTool extends BaseTool {
constructor(toolProps?: PublicToolProps, defaultToolProps?: ToolProps);
// (undocumented)
_activateDraw: (element: HTMLDivElement) => void;
// (undocumented)
_bounds: any;
// (undocumented)
_createMagnificationViewport: () => void;
// (undocumented)
_deactivateDraw: (element: HTMLDivElement) => void;
// (undocumented)
editData: {
referencedImageId: string;
viewportIdsToRender: string[];
enabledElement: Types_2.IEnabledElement;
renderingEngine: Types_2.IRenderingEngine;
currentPoints: IPoints;
} | null;
// (undocumented)
_getReferencedImageId(viewport: Types_2.IStackViewport | Types_2.IVolumeViewport): string;
// (undocumented)
mouseDragCallback: () => void;
// (undocumented)
_mouseDragCallback: (evt: EventTypes_2.MouseDragEventType) => void;
// (undocumented)
_mouseUpCallback: (evt: EventTypes_2.MouseUpEventType) => void;
// (undocumented)
preMouseDownCallback: (evt: EventTypes_2.MouseDownActivateEventType) => boolean;
// (undocumented)
static toolName: string;
}

declare namespace math {
export {
vec2,
Expand Down Expand Up @@ -3685,7 +3801,8 @@ declare namespace ToolSpecificAnnotationTypes {
RectangleROIThresholdAnnotation,
RectangleROIStartEndThresholdAnnotation,
PlanarFreehandROIAnnotation,
ArrowAnnotation
ArrowAnnotation,
AngleAnnotation
}
}

Expand Down
18 changes: 12 additions & 6 deletions packages/core/src/cache/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -451,13 +451,19 @@ class Cache implements ICache {
* @param imageId - Image ID
* @returns cached image
*/
public getCachedImageBasedOnImageURI(imageId: string): any {
const imageIdToUse = imageIdToURI(imageId);
const imageIdsInCache = Array.from(this._imageCache.keys());
public getCachedImageBasedOnImageURI(
imageId: string
): ICachedImage | undefined {
const imageURIToUse = imageIdToURI(imageId);

const cachedImageIds = Array.from(this._imageCache.keys());
const foundImageId = cachedImageIds.find((imageId) => {
return imageIdToURI(imageId) === imageURIToUse;
});

const foundImageId = imageIdsInCache.find(
(id) => id.indexOf(imageIdToUse) !== -1
);
if (!foundImageId) {
return;
}

return this._imageCache.get(foundImageId);
}
Expand Down
140 changes: 140 additions & 0 deletions packages/tools/examples/magnifyTool/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import { RenderingEngine, Types, Enums } from '@cornerstonejs/core';
import {
initDemo,
createImageIdsAndCacheMetaData,
setTitleAndDescription,
} from '../../../../utils/demo/helpers';
import * as cornerstoneTools from '@cornerstonejs/tools';

// This is for debugging purposes
console.warn(
'Click on index.ts to open source code for this example --------->'
);

const {
MagnifyTool,
PanTool,
ZoomTool,
ToolGroupManager,
Enums: csToolsEnums,
} = cornerstoneTools;

const { ViewportType } = Enums;
const { MouseBindings } = csToolsEnums;

// ======== Set up page ======== //
setTitleAndDescription(
'Magnify Tool',
'Magnify Tool to zoom in in part of the viewport (StackViewport only as of now)'
);

const content = document.getElementById('content');
const element = document.createElement('div');

// Disable right click context menu so we can have right click tools
element.oncontextmenu = (e) => e.preventDefault();

element.id = 'cornerstone-element';
element.style.width = '500px';
element.style.height = '500px';

content.appendChild(element);

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

content.append(instructions);
// ============================= //

const toolGroupId = 'STACK_TOOL_GROUP_ID';

/**
* Runs the demo
*/
async function run() {
// Init Cornerstone and related libraries
await initDemo();

// Add tools to Cornerstone3D
cornerstoneTools.addTool(MagnifyTool);
cornerstoneTools.addTool(PanTool);
cornerstoneTools.addTool(ZoomTool);

// Define a tool group, which defines how mouse events map to tool commands for
// Any viewport using the group
const toolGroup = ToolGroupManager.createToolGroup(toolGroupId);

// Add the tools to the tool group
toolGroup.addTool(MagnifyTool.toolName);
toolGroup.addTool(PanTool.toolName);
toolGroup.addTool(ZoomTool.toolName);

// Set the initial state of the tools, here we set one tool active on left click.
// This means left click will draw that tool.
toolGroup.setToolActive(MagnifyTool.toolName, {
bindings: [
{
mouseButton: MouseBindings.Primary, // Left Click
},
],
});

toolGroup.setToolActive(PanTool.toolName, {
bindings: [
{
mouseButton: MouseBindings.Auxiliary, // Middle Click
},
],
});

toolGroup.setToolActive(ZoomTool.toolName, {
bindings: [
{
mouseButton: MouseBindings.Secondary, //
},
],
});

// Get Cornerstone imageIds and fetch metadata into RAM
const imageIds = await createImageIdsAndCacheMetaData({
StudyInstanceUID:
'1.3.6.1.4.1.14519.5.2.1.7009.2403.334240657131972136850343327463',
SeriesInstanceUID:
'1.3.6.1.4.1.14519.5.2.1.7009.2403.226151125820845824875394858561',
wadoRsRoot: 'https://d1qmxk7r72ysft.cloudfront.net/dicomweb',
type: 'STACK',
});

// 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,
element,
};

renderingEngine.enableElement(viewportInput);

// Set the tool group on the viewport
toolGroup.addViewport(viewportId, renderingEngineId);

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

// Define a stack containing a single image
const stack = [imageIds[0]];

// Set the stack on the viewport
viewport.setStack(stack);

// Render the image
viewport.render();
}

run();
5 changes: 5 additions & 0 deletions packages/tools/examples/stackAnnotationTools/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const {
RectangleROITool,
EllipticalROITool,
BidirectionalTool,
AngleTool,
ToolGroupManager,
Enums: csToolsEnums,
} = cornerstoneTools;
Expand Down Expand Up @@ -57,6 +58,7 @@ const toolsNames = [
RectangleROITool.toolName,
EllipticalROITool.toolName,
BidirectionalTool.toolName,
AngleTool.toolName,
];
let selectedToolName = toolsNames[0];

Expand Down Expand Up @@ -95,6 +97,7 @@ async function run() {
cornerstoneTools.addTool(RectangleROITool);
cornerstoneTools.addTool(EllipticalROITool);
cornerstoneTools.addTool(BidirectionalTool);
cornerstoneTools.addTool(AngleTool);

// Define a tool group, which defines how mouse events map to tool commands for
// Any viewport using the group
Expand All @@ -106,6 +109,7 @@ async function run() {
toolGroup.addTool(RectangleROITool.toolName);
toolGroup.addTool(EllipticalROITool.toolName);
toolGroup.addTool(BidirectionalTool.toolName);
toolGroup.addTool(AngleTool.toolName);

// Set the initial state of the tools, here we set one tool active on left click.
// This means left click will draw that tool.
Expand All @@ -122,6 +126,7 @@ async function run() {
toolGroup.setToolPassive(RectangleROITool.toolName);
toolGroup.setToolPassive(EllipticalROITool.toolName);
toolGroup.setToolPassive(BidirectionalTool.toolName);
toolGroup.setToolPassive(AngleTool.toolName);

// Get Cornerstone imageIds and fetch metadata into RAM
const imageIds = await createImageIdsAndCacheMetaData({
Expand Down
5 changes: 5 additions & 0 deletions packages/tools/src/enums/ToolBindings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,19 @@
* See also: https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons
*/
enum MouseBindings {
/** usually the left button */
Primary = 1,
/** usually the right button */
Secondary = 2,
Primary_And_Secondary = 3,
/** usually mouse wheel button */
Auxiliary = 4,
Primary_And_Auxiliary = 5,
Secondary_And_Auxiliary = 6,
Primary_And_Secondary_And_Auxiliary = 7,
/** usually "Browser Back" button */
Fourth_Button = 8,
/** usually "Browser Forward" button */
Fifth_Button = 16,
}

Expand Down
4 changes: 4 additions & 0 deletions packages/tools/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ import {
RectangleROIStartEndThresholdTool,
SegmentationDisplayTool,
BrushTool,
AngleTool,
MagnifyTool,
} from './tools';

import * as Enums from './enums';
Expand Down Expand Up @@ -77,6 +79,8 @@ export {
BidirectionalTool,
PlanarFreehandROITool,
ArrowAnnotateTool,
AngleTool,
MagnifyTool,
// Segmentation Display
SegmentationDisplayTool,
// Segmentation Editing Tools
Expand Down
Loading

0 comments on commit 2c4c800

Please sign in to comment.