Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
142 changes: 142 additions & 0 deletions extensions/cornerstone/src/commandsModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,50 @@ function commandsModule({
}
}

function _clampToRange(value: number, min: number, max: number) {
return Math.min(Math.max(value, min), max);
}

function _getRegionSegmentPlusLimits() {
const enabledElement = _getActiveViewportEnabledElement();
const viewport = enabledElement?.viewport;

if (!viewport) {
return {
maxDeltaK: 25,
maxDeltaIJ: 100,
};
}

let maxDeltaK = 25;
let maxDeltaIJ = 100;

if (viewport instanceof StackViewport) {
maxDeltaK = Math.max(1, viewport.getImageIds()?.length ?? 1);
} else if (viewport instanceof VolumeViewport) {
const sliceData = csUtils.getImageSliceDataForVolumeViewport(viewport);
maxDeltaK = Math.max(1, sliceData?.numberOfSlices ?? 1);
}

const imageData = viewport.getImageData?.();
const dimensions =
imageData?.imageData?.getDimensions?.() ??
imageData?.dimensions ??
(Array.isArray(imageData) ? imageData : undefined);
Comment thread
greptile-apps[bot] marked this conversation as resolved.

if (Array.isArray(dimensions) && dimensions.length >= 2) {
maxDeltaIJ = Math.max(1, dimensions[0], dimensions[1]);
if (dimensions.length >= 3) {
maxDeltaK = Math.max(maxDeltaK, dimensions[2] || 1);
}
}

return {
maxDeltaK,
maxDeltaIJ,
};
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

/**
* Creates a command function that sets a style property for segmentation types.
* If type is provided, sets the property for that type only.
Expand Down Expand Up @@ -1988,6 +2032,37 @@ function commandsModule({
},
rejectPreview: () => {
actions._handlePreviewAction('reject');
// ESC is commonly bound to rejectPreview in OHIF.
// Also cancel any in-flight tool operation so non-preview tools
// (e.g., one-click flood fill) can be interrupted consistently.
actions.cancelMeasurement();
Comment thread
greptile-apps[bot] marked this conversation as resolved.
},
cancelMeasurement: () => {
const enabledElement = _getActiveViewportEnabledElement();
const element = enabledElement?.viewport?.element;
const viewportId = viewportGridService.getActiveViewportId();

let cancelled = false;

if (element) {
const cancelledAnnotationUID = cornerstoneTools.cancelActiveManipulations(element);
cancelled = !!cancelledAnnotationUID;
}

const toolGroup = toolGroupService.getToolGroupForViewport(viewportId);
const activeToolName = toolGroupService.getActiveToolForViewport(viewportId);
const activeToolInstance = activeToolName
? toolGroup?.getToolInstance(activeToolName)
: undefined;

if (activeToolInstance && typeof activeToolInstance.cancelActiveOperation === 'function') {
cancelled = activeToolInstance.cancelActiveOperation() || cancelled;
}

if (cancelled) {
const renderingEngine = cornerstoneViewportService.getRenderingEngine();
renderingEngine.render();
}
},
clearMarkersForMarkerLabelmap: () => {
const { viewport } = _getActiveViewportEnabledElement();
Expand Down Expand Up @@ -2071,6 +2146,71 @@ function commandsModule({
});
}
},
setRegionSegmentPlusFloodFillConfiguration: ({ value, id, options }) => {
const viewportId = viewportGridService.getActiveViewportId();
const toolGroupId = toolGroupService.getToolGroupForViewport(viewportId)?.id;
const toolGroupIds = toolGroupId ? [toolGroupId] : toolGroupService.getToolGroupIds();

const maxDeltaKOptionId = 'region-segment-plus-max-delta-k';
const maxDeltaIJOptionId = 'region-segment-plus-max-delta-ij';
const toolButton = toolbarService.getButton('RegionSegmentPlus');
const buttonOptions = toolButton?.props?.options;
const optionList =
(Array.isArray(options) && options.length ? options : buttonOptions) ?? [];

const maxDeltaKOption = optionList.find(option => option.id === maxDeltaKOptionId);
const maxDeltaIJOption = optionList.find(option => option.id === maxDeltaIJOptionId);
const { maxDeltaK: maxAllowedK, maxDeltaIJ: maxAllowedIJ } = _getRegionSegmentPlusLimits();

if (maxDeltaKOption) {
maxDeltaKOption.max = maxAllowedK;
}
if (maxDeltaIJOption) {
maxDeltaIJOption.max = maxAllowedIJ;
}

const incomingValue = Number(value);
const currentK = Number(maxDeltaKOption?.value ?? 25);
const currentIJ = Number(maxDeltaIJOption?.value ?? 100);

Comment thread
greptile-apps[bot] marked this conversation as resolved.
const nextMaxDeltaK = _clampToRange(
id === maxDeltaKOptionId && Number.isFinite(incomingValue) ? incomingValue : currentK,
1,
maxAllowedK
);
const nextMaxDeltaIJ = _clampToRange(
id === maxDeltaIJOptionId && Number.isFinite(incomingValue) ? incomingValue : currentIJ,
1,
maxAllowedIJ
);

if (maxDeltaKOption) {
maxDeltaKOption.value = nextMaxDeltaK;
}
if (maxDeltaIJOption) {
maxDeltaIJOption.value = nextMaxDeltaIJ;
}

for (const tgId of toolGroupIds) {
const toolGroup = toolGroupService.getToolGroup(tgId);
if (!toolGroup?.hasTool(toolNames.RegionSegmentPlus)) {
continue;
}

toolGroup.setToolConfiguration(toolNames.RegionSegmentPlus, {
segmentationMode: 'floodfill_full',
hoverPrecheckEnabled: false,
intensityRangeStrategy: 'canvasDiskTriClassLarge',
maxDeltaK: nextMaxDeltaK,
maxDeltaIJ: nextMaxDeltaIJ,
preview: {
enabled: false,
},
});
}

toolbarService.refreshToolbarState({ viewportId });
},
increaseBrushSize: () => {
_handleBrushSizeAction('increase');
},
Expand Down Expand Up @@ -2739,12 +2879,14 @@ function commandsModule({
toggleSegmentSelect: actions.toggleSegmentSelect,
acceptPreview: actions.acceptPreview,
rejectPreview: actions.rejectPreview,
cancelMeasurement: actions.cancelMeasurement,
toggleUseCenterSegmentIndex: actions.toggleUseCenterSegmentIndex,
toggleLabelmapAssist: actions.toggleLabelmapAssist,
interpolateScrollForMarkerLabelmap: actions.interpolateScrollForMarkerLabelmap,
clearMarkersForMarkerLabelmap: actions.clearMarkersForMarkerLabelmap,
setBrushSize: actions.setBrushSize,
setThresholdRange: actions.setThresholdRange,
setRegionSegmentPlusFloodFillConfiguration: actions.setRegionSegmentPlusFloodFillConfiguration,
increaseBrushSize: actions.increaseBrushSize,
decreaseBrushSize: actions.decreaseBrushSize,
addNewSegment: actions.addNewSegment,
Expand Down
10 changes: 10 additions & 0 deletions modes/segmentation/src/initToolGroups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,16 @@ function createTools({ utilityModule, commandsManager }) {
},
{
toolName: toolNames.RegionSegmentPlus,
configuration: {
segmentationMode: 'floodfill_full',
hoverPrecheckEnabled: false,
intensityRangeStrategy: 'canvasDiskTriClassLarge',
maxDeltaK: 25,
maxDeltaIJ: 100,
preview: {
enabled: false,
},
},
},
{
toolName: 'CircularEraser',
Expand Down
29 changes: 29 additions & 0 deletions modes/segmentation/src/toolbarButtons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -784,13 +784,42 @@ export const toolbarButtons: Button[] = [
],
commands: [
'setToolActiveToolbar',
'setRegionSegmentPlusFloodFillConfiguration',
{
commandName: 'activateSelectedSegmentationOfType',
commandOptions: {
segmentationRepresentationType: 'Labelmap',
},
},
],
options: [
{
name: i18n.t('Buttons:Max Delta K'),
id: 'region-segment-plus-max-delta-k',
type: 'range',
explicitRunOnly: true,
min: 1,
max: 1000,
step: 1,
value: 25,
commands: {
commandName: 'setRegionSegmentPlusFloodFillConfiguration',
},
},
{
name: i18n.t('Buttons:Max Delta IJ'),
id: 'region-segment-plus-max-delta-ij',
type: 'range',
explicitRunOnly: true,
min: 1,
max: 4096,
step: 1,
value: 100,
commands: {
commandName: 'setRegionSegmentPlusFloodFillConfiguration',
},
},
],
},
},
{
Expand Down
Loading