Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added MIP option to StackScrollTool #537

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
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: 4 additions & 0 deletions common/reviews/api/tools.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -4678,6 +4678,8 @@ export class StackScrollMouseWheelTool extends BaseTool {
export class StackScrollTool extends BaseTool {
constructor(toolProps?: PublicToolProps, defaultToolProps?: ToolProps);
// (undocumented)
deltaX: number;
// (undocumented)
deltaY: number;
// (undocumented)
_dragCallback(evt: EventTypes_2.InteractionEventType): void;
Expand All @@ -4691,6 +4693,8 @@ export class StackScrollTool extends BaseTool {
static toolName: any;
// (undocumented)
touchDragCallback(evt: EventTypes_2.InteractionEventType): void;
// (undocumented)
_triggerMIP(viewport: any, delta: any, invert: any): void;
}

// @public
Expand Down
44 changes: 41 additions & 3 deletions packages/tools/examples/volumeViewportSynchronization/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
createImageIdsAndCacheMetaData,
setTitleAndDescription,
addToggleButtonToToolbar,
addDropdownToToolbar,
} from '../../../../utils/demo/helpers';
import * as cornerstoneTools from '@cornerstonejs/tools';

Expand All @@ -24,6 +25,7 @@ const {
ZoomTool,
ToolGroupManager,
StackScrollMouseWheelTool,
StackScrollTool,
Enums: csToolsEnums,
synchronizers,
SynchronizerManager,
Expand Down Expand Up @@ -97,6 +99,33 @@ Toggle the controls to add viewports to the synchronization groups.

content.append(instructions);
// ============================= //
const toolGroupId = 'TOOL_GROUP_ID';
const leftClickTools = [WindowLevelTool.toolName, StackScrollTool.toolName];
const defaultLeftClickTool = leftClickTools[0];
let currentLeftClickTool = leftClickTools[0];
addDropdownToToolbar({
options: {
values: leftClickTools,
defaultValue: defaultLeftClickTool,
},
onSelectedValueChange: (selectedValue) => {
console.log('>>>>> selectedValue :: ', selectedValue);
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId);
console.log('>>>>> toolGroup :: ', toolGroup);

toolGroup.setToolPassive(currentLeftClickTool);

toolGroup.setToolActive(<string>selectedValue, {
bindings: [
{
mouseButton: MouseBindings.Primary, // Left Click
},
],
});

currentLeftClickTool = selectedValue;
},
});

const SynchronizerButtonInfo = [
{ viewportLabel: 'A', viewportId: viewportIds[0] },
Expand Down Expand Up @@ -151,23 +180,32 @@ async function run() {
// Init Cornerstone and related libraries
await initDemo();

const toolGroupId = 'TOOL_GROUP_ID';

// Add tools to Cornerstone3D
cornerstoneTools.addTool(PanTool);
cornerstoneTools.addTool(WindowLevelTool);
cornerstoneTools.addTool(StackScrollMouseWheelTool);
cornerstoneTools.addTool(StackScrollTool);
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 tools to the tool group
toolGroup.addTool(WindowLevelTool.toolName, { volumeId });
toolGroup.addTool(WindowLevelTool.toolName);
toolGroup.addTool(PanTool.toolName);
toolGroup.addTool(ZoomTool.toolName);
toolGroup.addTool(StackScrollMouseWheelTool.toolName);
toolGroup.addTool(StackScrollTool.toolName, {
leftRightMode: false,
mipMode: {
enabled: true,
invert: false,
pixelsPerThickness: 5,
minSlabThickness: 5e-2,
maxSlabThickness: 30,
},
Comment on lines +200 to +207
Copy link
Member

Choose a reason for hiding this comment

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

I guess this example should get updated too right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yep still need to push the changes

});

// Set the initial state of the tools, here all tools are active and bound to
// Different mouse inputs
Expand Down
108 changes: 95 additions & 13 deletions packages/tools/src/tools/StackScrollTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,30 @@ import { PublicToolProps, ToolProps, EventTypes } from '../types';
class StackScrollTool extends BaseTool {
static toolName;
deltaY: number;
deltaX: number;
constructor(
toolProps: PublicToolProps = {},
defaultToolProps: ToolProps = {
supportedInteractionTypes: ['Mouse', 'Touch'],
configuration: {
invert: false,
leftRightMode: false,
Copy link
Member

@sedghi sedghi Apr 5, 2023

Choose a reason for hiding this comment

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

what is the difference between leftRightMode and mipMode? can leftRightMode be not mipMode? to me this configuration is complex and not clear at the first glance. there are multiple levels that has enabled, how about something like

configuration: {
  activeMode: 'stackScrollMode', // or 'mipMode'
  stackScrollMode: {
    invert: false,
    loop: false,
    debounceIfNotLoaded: true,
  },
  mipMode: {
    pixelsPerThickness: 5,
    minSlabThickness: 5e-2,
    maxSlabThickness: 30,
  },
},

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So the following options need some way to be selected...

leftRight vs upDown
Inversion is rightLeft vs downUp
Then MIP can be active/inactive
StackScroll can be active/inactive

Both MIP and StackScroll can be active at the same time.

What about something like this:

configuration: {
  direction: {
   stackScrollMode: enums['upDown', 'downUp', null]
   mipMode: enums['leftRight', 'rightLeft', null]
  },
  stackScrollMode: {
    loop: false,
    debounceIfNotLoaded: true,
  },
  mipMode: {
    pixelsPerThickness: 5,
    minSlabThickness: 5e-2,
    maxSlabThickness: 30,
  },
},

Copy link
Member

Choose a reason for hiding this comment

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

how about we stick to horizontal and vertical and assume (naturally) that right is more and up is more, and we have a invert flag?

configuration: {
  stackScrollMode: {
    enabled: true,
	invert: true, 
    direction: 'vertical'
    loop: false,
    debounceIfNotLoaded: true,
  },
  mipMode: {
    enabled: false, 
    invert: true, 
    direction: 'horizontal'
    pixelsPerThickness: 5,
    minSlabThickness: 5e-2,
    maxSlabThickness: 30,
  },
},

Copy link
Contributor Author

Choose a reason for hiding this comment

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

works for me!

debounceIfNotLoaded: true,
loop: false
loop: false,
stackScrollEnabled: true,
mipMode: {
enabled: true,
invert: false,
pixelsPerThickness: 5,
minSlabThickness: 5e-2,
maxSlabThickness: 30,
},
},
}
) {
super(toolProps, defaultToolProps);
this.deltaY = 1;
this.deltaX = 1;
}

mouseDragCallback(evt: EventTypes.InteractionEventType) {
Expand All @@ -42,9 +53,16 @@ class StackScrollTool extends BaseTool {
const { viewport } = getEnabledElementByIds(viewportId, renderingEngineId);

const targetId = this.getTargetId(viewport);
const { debounceIfNotLoaded, invert, loop } = this.configuration;
const {
debounceIfNotLoaded,
invert,
loop,
leftRightMode,
stackScrollEnabled,
} = this.configuration;

const deltaPointY = deltaPoints.canvas[1];
const deltaPointX = deltaPoints.canvas[0];

let volumeId;
if (viewport instanceof VolumeViewport) {
Expand All @@ -53,24 +71,66 @@ class StackScrollTool extends BaseTool {

const pixelsPerImage = this._getPixelPerImage(viewport);
const deltaY = deltaPointY + this.deltaY;
const deltaX = deltaPointX + this.deltaX;

if (!pixelsPerImage) {
return;
}

if (Math.abs(deltaY) >= pixelsPerImage) {
const imageIdIndexOffset = Math.round(deltaY / pixelsPerImage);
if (stackScrollEnabled && !leftRightMode) {
if (Math.abs(deltaY) >= pixelsPerImage) {
const imageIdIndexOffset = Math.round(deltaY / pixelsPerImage);

scroll(viewport, {
delta: invert ? -imageIdIndexOffset : imageIdIndexOffset,
volumeId,
debounceLoading: debounceIfNotLoaded,
loop: loop
});
scroll(viewport, {
delta: invert ? -imageIdIndexOffset : imageIdIndexOffset,
volumeId,
debounceLoading: debounceIfNotLoaded,
loop: loop,
});

this.deltaY = deltaY % pixelsPerImage;
} else {
this.deltaY = deltaY;
this.deltaY = deltaY % pixelsPerImage;
} else {
this.deltaY = deltaY;
}
}

if (stackScrollEnabled && leftRightMode) {
if (Math.abs(deltaX) >= pixelsPerImage) {
const imageIdIndexOffset = Math.round(deltaX / pixelsPerImage);

scroll(viewport, {
delta: invert ? -imageIdIndexOffset : imageIdIndexOffset,
volumeId,
debounceLoading: debounceIfNotLoaded,
loop: loop,
});

this.deltaX = deltaX % pixelsPerImage;
} else {
this.deltaX = deltaX;
}
}

const { mipMode } = this.configuration;
if (!mipMode?.enabled) return;
const { pixelsPerThickness, mipModeInvert } = mipMode;

if (mipMode?.enabled && leftRightMode) {
if (Math.abs(deltaY) >= pixelsPerThickness) {
this._triggerMIP(viewport, deltaY > 0 ? -1 : 1, mipModeInvert);
this.deltaY = deltaY % pixelsPerThickness;
} else {
this.deltaY = deltaY;
}
}

if (mipMode?.enabled && !leftRightMode) {
if (Math.abs(deltaX) >= pixelsPerThickness) {
this._triggerMIP(viewport, deltaX > 0 ? 1 : -1, mipModeInvert);
this.deltaX = deltaX % pixelsPerThickness;
} else {
this.deltaX = deltaX;
}
}
}

Expand All @@ -91,6 +151,28 @@ class StackScrollTool extends BaseTool {
return viewport.getImageIds().length;
}
}

_triggerMIP(viewport, delta, invert) {
const inversionValue = invert ? -1 : 1;
const { minSlabThickness, maxSlabThickness } = this.configuration.mipMode;
if (viewport instanceof VolumeViewport) {
const slabThickness = Math.min(
maxSlabThickness,
viewport.getSlabThickness() + inversionValue * delta
);
if (slabThickness <= minSlabThickness) {
viewport.setBlendMode(0);
viewport.setSlabThickness(minSlabThickness);
viewport.render();
} else {
viewport.setBlendMode(1);
viewport.setSlabThickness(
slabThickness >= maxSlabThickness ? maxSlabThickness : slabThickness
);
viewport.render();
}
}
}
}

StackScrollTool.toolName = 'StackScroll';
Expand Down