Skip to content
Open
Show file tree
Hide file tree
Changes from all 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),
});

// 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
60 changes: 52 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,18 @@ 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(desiredVolumeId: string) {
// imageId including the target id is a proxy for testing if the
// image id is a member of that volume. This may need to be fixed in the
// future to add more criteria.
return (_viewport, { imageId }) => imageId.includes(desiredVolumeId);
}

/**
* Newer method for getting the tool name as a property
*/
Expand Down Expand Up @@ -228,18 +254,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 [imageId, cachedStat] of Object.entries(data.cachedStats)) {
if (isPreferredTargetId(viewport, { imageId, cachedStat })) {
return imageId;
}
}
}

// 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
34 changes: 34 additions & 0 deletions packages/tools/src/types/IBaseTool.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,37 @@
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,
/**
* The target info is a specifier for different types of target information.
* Right now there is just the single option consisting of an image id and
* cached stat, but in the future other alternatives might be provided.
*/
targetInfo: {
/**
* The imageId of a cachedStat instance. This isn't the only way to
* identify data, but is one possible option.
*/
imageId: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
cachedStat: any;
}
) => boolean;
}

export type IBaseTool = BaseTool;
Loading