Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion packages/tools/examples/petCt/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,9 @@ function setUpToolGroups() {
// Only set CT volume to MIP in the fusion viewport
filterActorUIDsToSetSlabThickness: [ctVolumeId],
});
fusionToolGroup.addTool(RectangleROITool.toolName);
fusionToolGroup.addTool(RectangleROITool.toolName, {
isPreferredTargetId: RectangleROITool.isSpecifiedTargetId(ptVolumeId),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is confusing api, what are we achieving here?

isPreferredTargetId: RectangleROITool.isSpecifiedTargetId(ptVolumeId)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is saying that the preferred target for statistics is one belonging to the specified volume id. If you have better naming on that, please let me know.

});

// Here is the difference in the toolGroups used, that we need to specify the
// volume to use for the WindowLevelTool for the fusion viewports
Expand Down
3 changes: 1 addition & 2 deletions packages/tools/src/tools/annotation/CircleROITool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -607,8 +607,6 @@ class CircleROITool extends AnnotationTool {
return renderStatus;
}

const targetId = this.getTargetId(viewport);

const renderingEngine = viewport.getRenderingEngine();

const styleSpecifier: StyleSpecifier = {
Expand All @@ -623,6 +621,7 @@ class CircleROITool extends AnnotationTool {
const { handles } = data;
const { points, activeHandleIndex } = handles;

const targetId = this.getTargetId(viewport, data);
styleSpecifier.annotationUID = annotationUID;

const { color, lineWidth, lineDash } = this.getAnnotationStyle({
Expand Down
2 changes: 1 addition & 1 deletion packages/tools/src/tools/annotation/CobbAngleTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -667,7 +667,6 @@ class CobbAngleTool extends AnnotationTool {
return renderStatus;
}

const targetId = this.getTargetId(viewport);
const renderingEngine = viewport.getRenderingEngine();

const styleSpecifier: StyleSpecifier = {
Expand All @@ -682,6 +681,7 @@ class CobbAngleTool extends AnnotationTool {
const { annotationUID, data } = annotation;
const { points, activeHandleIndex } = data.handles;

const targetId = this.getTargetId(viewport, data);
styleSpecifier.annotationUID = annotationUID;

const { color, lineWidth, lineDash } = this.getAnnotationStyle({
Expand Down
2 changes: 1 addition & 1 deletion packages/tools/src/tools/annotation/DragProbeTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,6 @@ class DragProbeTool extends ProbeTool {
return renderStatus;
}

const targetId = this.getTargetId(viewport);
const renderingEngine = viewport.getRenderingEngine();

const styleSpecifier: StyleSpecifier = {
Expand All @@ -151,6 +150,7 @@ class DragProbeTool extends ProbeTool {
const point = data.handles.points[0];
const canvasCoordinates = viewport.worldToCanvas(point);

const targetId = this.getTargetId(viewport, data);
styleSpecifier.annotationUID = annotationUID;

const { color } = this.getAnnotationStyle({
Expand Down
3 changes: 1 addition & 2 deletions packages/tools/src/tools/annotation/EllipticalROITool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -773,8 +773,6 @@ class EllipticalROITool extends AnnotationTool {
return renderStatus;
}

const targetId = this.getTargetId(viewport);

const renderingEngine = viewport.getRenderingEngine();

const styleSpecifier: StyleSpecifier = {
Expand All @@ -789,6 +787,7 @@ class EllipticalROITool extends AnnotationTool {
const { handles } = data;
const { points, activeHandleIndex } = handles;

const targetId = this.getTargetId(viewport, data);
styleSpecifier.annotationUID = annotationUID;

const { color, lineWidth, lineDash } = this.getAnnotationStyle({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1064,7 +1064,7 @@ class PlanarFreehandROITool extends ContourSegmentationBaseTool {
svgDrawingHelper
) => {
const { data } = <PlanarFreehandROIAnnotation>annotation;
const targetId = this.getTargetId(viewport);
const targetId = this.getTargetId(viewport, data);

const styleSpecifier: AnnotationStyle.StyleSpecifier = {
toolGroupId: this.toolGroupId,
Expand Down
2 changes: 1 addition & 1 deletion packages/tools/src/tools/annotation/ProbeTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,6 @@ class ProbeTool extends AnnotationTool {
return renderStatus;
}

const targetId = this.getTargetId(viewport);
const renderingEngine = viewport.getRenderingEngine();

const styleSpecifier: StyleSpecifier = {
Expand All @@ -452,6 +451,7 @@ class ProbeTool extends AnnotationTool {
const point = data.handles.points[0];
const canvasCoordinates = viewport.worldToCanvas(point);

const targetId = this.getTargetId(viewport, data);
styleSpecifier.annotationUID = annotationUID;

const { color, lineWidth } = this.getAnnotationStyle({
Expand Down
2 changes: 1 addition & 1 deletion packages/tools/src/tools/annotation/RectangleROITool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -607,7 +607,6 @@ class RectangleROITool extends AnnotationTool {
return renderStatus;
}

const targetId = this.getTargetId(viewport);
const renderingEngine = viewport.getRenderingEngine();

const styleSpecifier: StyleSpecifier = {
Expand All @@ -622,6 +621,7 @@ class RectangleROITool extends AnnotationTool {
const { points, activeHandleIndex } = data.handles;
const canvasCoordinates = points.map((p) => viewport.worldToCanvas(p));

const targetId = this.getTargetId(viewport, data);
styleSpecifier.annotationUID = annotationUID;

const { color, lineWidth, lineDash } = this.getAnnotationStyle({
Expand Down
57 changes: 49 additions & 8 deletions packages/tools/src/tools/base/BaseTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ import { utilities } from '@cornerstonejs/core';
import type { Types } from '@cornerstonejs/core';
import ToolModes from '../../enums/ToolModes';
import type StrategyCallbacks from '../../enums/StrategyCallbacks';
import type { InteractionTypes, ToolProps, PublicToolProps } from '../../types';
import type {
InteractionTypes,
ToolProps,
PublicToolProps,
ToolConfiguration,
} from '../../types';

const { DefaultHistoryMemo } = utilities.HistoryMemo;

Expand All @@ -15,8 +20,17 @@ abstract class BaseTool {
static toolName;
/** Supported Interaction Types - currently only Mouse */
public supportedInteractionTypes: InteractionTypes[];
/**
* The configuration for this tool.
* IBaseTool contains some default configuration values, and you can use
* configurationTyped to get the typed version of this.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public configuration: Record<string, any>;
public get configurationTyped() {
return <ToolConfiguration>this.configuration;
}

/** ToolGroup ID the tool instance belongs to */
public toolGroupId: string;
/** Tool Mode - Active/Passive/Enabled/Disabled/ */
Expand Down Expand Up @@ -76,6 +90,15 @@ abstract class BaseTool {
return utilities.deepMerge(defaultProps, additionalProps);
}

/**
* A function generator to test if the target id is the desired one.
* Used for deciding which set of cached stats is appropriate to display
* for a given viewport.
*/
public static isSpecifiedTargetId(desiredTargetId: string) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Celian-abd - my preference is to provide a function that determines the preferred target id rather than hard coding the value. That way it is possible to have more flexible determination and to work with multiple merged viewports at a single time by creating a custom function that works against both viewports.

return (_viewport, { targetId }) => targetId.includes(desiredTargetId);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sedghi - this code is wrong as the target id doesn't have to include the desired target id as a substring in general. The right solution is to get the volume id associated with the given image id, and check for equality. HOwever, without passing in the set of image ids associated with the volume, I don't see an easy way of doing that. Should I just pass in a Set of the image ids that are preferred and use imageIds.has(imageId)?

}

/**
* Newer method for getting the tool name as a property
*/
Expand Down Expand Up @@ -228,18 +251,36 @@ abstract class BaseTool {
/**
* Get the target Id for the viewport which will be used to store the cached
* statistics scoped to that target in the annotations.
* For StackViewport, targetId is the viewportId, but for the volume viewport,
* the targetId will be grabbed from the volumeId if particularly specified
* in the tool configuration, or if not, the first actorUID in the viewport.
* For StackViewport, targetId is usually derived from the imageId.
* For VolumeViewport, it's derived from the volumeId.
* This method allows prioritizing a specific volumeId from the tool's
* configuration if available in the cachedStats.
*
* @param viewport - viewport to get the targetId for
* @param data - Optional: The annotation's data object, containing cachedStats.
* @returns targetId
*/
protected getTargetId(viewport: Types.IViewport): string | undefined {
const targetId = viewport.getViewReferenceId?.();
if (targetId) {
return targetId;
protected getTargetId(
viewport: Types.IViewport,
data?: unknown & { cachedStats?: Record<string, unknown> }
): string | undefined {
const { isPreferredTargetId } = this.configurationTyped; // Get preferred ID from config

// Check if cachedStats is available and contains the preferredVolumeId
if (isPreferredTargetId && data?.cachedStats) {
for (const [targetId, cachedStat] of Object.entries(data.cachedStats)) {
if (isPreferredTargetId(viewport, { targetId, cachedStat })) {
return targetId;
}
}
}

// If not found or not applicable, use the viewport's default method
const defaultTargetId = viewport.getViewReferenceId?.();
if (defaultTargetId) {
return defaultTargetId;
}

throw new Error(
'getTargetId: viewport must have a getViewReferenceId method'
);
Expand Down
25 changes: 25 additions & 0 deletions packages/tools/src/types/IBaseTool.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,28 @@
import type BaseTool from '../tools/base/BaseTool';

/**
* General tool configuration. This is intended to be extended
* by various tools to add the different configuration options.
*/
export interface ToolConfiguration {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
strategies: any;
defaultStrategy?: string;
activeStrategy?: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
strategyOptions: any;

/**
* @returns true if the given targetId is preferred.
*/
isPreferredTargetId?: (
viewport,
targetInfo: {
targetId: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
cachedStat?: any;
}
) => boolean;
}

export type IBaseTool = BaseTool;
Loading