update
This commit is contained in:
+85
-26
@@ -7,7 +7,7 @@ import {
|
||||
} from "@cornerstonejs/core"
|
||||
import { init as csRenderInit } from "@cornerstonejs/core"
|
||||
import { init as csToolsInit, addTool, ToolGroupManager, Enums as csToolsEnums, PanTool, WindowLevelTool, StackScrollTool, ZoomTool, PlanarRotateTool, utilities as csToolsUtilities } from "@cornerstonejs/tools"
|
||||
import { LengthTool, ProbeTool, ArrowAnnotateTool, RectangleROITool, EllipticalROITool, PlanarFreehandROITool, PlanarFreehandContourSegmentationTool, SculptorTool, CrosshairsTool, ReferenceLinesTool, ReferenceCursors } from "@cornerstonejs/tools"
|
||||
import { LengthTool, ProbeTool, ArrowAnnotateTool, RectangleROITool, EllipticalROITool, PlanarFreehandROITool, SculptorTool, CrosshairsTool, ReferenceLinesTool, ReferenceCursors } from "@cornerstonejs/tools"
|
||||
import { init as dicomImageLoaderInit } from "@cornerstonejs/dicom-image-loader"
|
||||
import * as cornerstoneTools from '@cornerstonejs/tools';
|
||||
import * as cornerstone3d from '@cornerstonejs/core';
|
||||
@@ -23,6 +23,26 @@ import { getLoggerConfig, logger, parseLogFormat, parseLogLevel, setLoggerConfig
|
||||
import { NoLabelArrowAnnotateTool } from "./NoLabelArrowAnnotateTool";
|
||||
import { render } from "@cornerstonejs/tools/tools/displayTools/Labelmap/labelmapDisplay"
|
||||
|
||||
const windowLevelToolPrototype = WindowLevelTool.prototype as any;
|
||||
|
||||
if (!windowLevelToolPrototype._mouseSensitivityPatched) {
|
||||
const originalGetNewRange = windowLevelToolPrototype.getNewRange;
|
||||
windowLevelToolPrototype.getNewRange = function ({ deltaPointsCanvas, ...rest }: any) {
|
||||
const sensitivity = typeof this?.configuration?.mouseSensitivity === "number"
|
||||
? this.configuration.mouseSensitivity
|
||||
: 1;
|
||||
const scaledDeltaPointsCanvas = Array.isArray(deltaPointsCanvas)
|
||||
? [deltaPointsCanvas[0] * sensitivity, deltaPointsCanvas[1] * sensitivity]
|
||||
: deltaPointsCanvas;
|
||||
|
||||
return originalGetNewRange.call(this, {
|
||||
...rest,
|
||||
deltaPointsCanvas: scaledDeltaPointsCanvas,
|
||||
});
|
||||
};
|
||||
windowLevelToolPrototype._mouseSensitivityPatched = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Extend the Window interface to include cornerstoneTools
|
||||
@@ -152,7 +172,6 @@ const TOOL_OPTIONS = [
|
||||
{ label: "Rectangle ROI", value: RectangleROITool.toolName, icon: "▭" },
|
||||
{ label: "Ellipse ROI", value: EllipticalROITool.toolName, icon: "◯" },
|
||||
{ label: "Freehand ROI", value: PlanarFreehandROITool.toolName, icon: "✏️" },
|
||||
{ label: "Freehand Contour Segmentation", value: PlanarFreehandContourSegmentationTool.toolName, icon: "🖊️" },
|
||||
{ label: "Sculptor", value: SculptorTool.toolName, icon: "🔧" },
|
||||
{ label: "Reference Cursors", value: ReferenceCursors.toolName, icon: "⦿" },
|
||||
];
|
||||
@@ -433,6 +452,7 @@ export default function App({ container_id, imageStacks, autoCacheStack, annotat
|
||||
|
||||
const MOUSE_SETTINGS_KEY = "dv3d_mouseToolBindings";
|
||||
const CTRL_MOUSE_SETTINGS_KEY = "dv3d_ctrlMouseToolBindings";
|
||||
const WINDOW_LEVEL_SENSITIVITY_KEY = "dv3d_windowLevelMouseSensitivity";
|
||||
const initialLogMinLevel = (initialLoggerConfig?.minLevel as LogLevel | undefined)
|
||||
?? (initialLoggerConfig?.debug !== undefined ? (initialLoggerConfig.debug ? "debug" : "warn") : undefined)
|
||||
?? getLoggerConfig().minLevel;
|
||||
@@ -2122,6 +2142,7 @@ export default function App({ container_id, imageStacks, autoCacheStack, annotat
|
||||
Auxiliary: ProbeTool.toolName,
|
||||
Secondary: NoLabelArrowAnnotateTool.toolName,
|
||||
})
|
||||
const [windowLevelMouseSensitivity, setWindowLevelMouseSensitivity] = useState(1)
|
||||
|
||||
// Track if Ctrl is pressed
|
||||
const [ctrlPressed, setCtrlPressed] = useState(false)
|
||||
@@ -2302,6 +2323,13 @@ export default function App({ container_id, imageStacks, autoCacheStack, annotat
|
||||
const ctrl = localStorage.getItem(CTRL_MOUSE_SETTINGS_KEY);
|
||||
if (mouse) setMouseToolBindings(JSON.parse(mouse));
|
||||
if (ctrl) setCtrlMouseToolBindings(JSON.parse(ctrl));
|
||||
const rawWindowLevelSensitivity = localStorage.getItem(WINDOW_LEVEL_SENSITIVITY_KEY);
|
||||
if (rawWindowLevelSensitivity) {
|
||||
const parsedSensitivity = JSON.parse(rawWindowLevelSensitivity);
|
||||
if (typeof parsedSensitivity?.mouseSensitivity === "number") {
|
||||
setWindowLevelMouseSensitivity(parsedSensitivity.mouseSensitivity);
|
||||
}
|
||||
}
|
||||
|
||||
const rawLogging = localStorage.getItem(LOG_SETTINGS_KEY);
|
||||
if (rawLogging) {
|
||||
@@ -2351,6 +2379,15 @@ export default function App({ container_id, imageStacks, autoCacheStack, annotat
|
||||
} catch (e) { }
|
||||
}, [ctrlMouseToolBindings]);
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
localStorage.setItem(
|
||||
WINDOW_LEVEL_SENSITIVITY_KEY,
|
||||
JSON.stringify({ mouseSensitivity: windowLevelMouseSensitivity })
|
||||
);
|
||||
} catch (e) { }
|
||||
}, [windowLevelMouseSensitivity]);
|
||||
|
||||
useEffect(() => {
|
||||
setLoggerConfig({
|
||||
minLevel: loggingMinLevel,
|
||||
@@ -2479,7 +2516,7 @@ export default function App({ container_id, imageStacks, autoCacheStack, annotat
|
||||
'RectangleROI',
|
||||
'EllipticalROI',
|
||||
'PlanarFreehandROI',
|
||||
'PlanarFreehandContourSegmentation',
|
||||
'Sculptor',
|
||||
// Add more if needed
|
||||
].forEach(toolName => {
|
||||
mainToolGroup.setToolConfiguration(toolName, {
|
||||
@@ -2604,6 +2641,12 @@ export default function App({ container_id, imageStacks, autoCacheStack, annotat
|
||||
toolGroup.setToolActive(tool, { bindings });
|
||||
});
|
||||
|
||||
if (typeof toolGroup.setToolConfiguration === "function") {
|
||||
toolGroup.setToolConfiguration(WindowLevelTool.toolName, {
|
||||
mouseSensitivity: windowLevelMouseSensitivity,
|
||||
});
|
||||
}
|
||||
|
||||
// Add touch bindings so tools are usable on phones/tablets.
|
||||
const primaryTool = mouseToolBindings.Primary;
|
||||
if (primaryTool) {
|
||||
@@ -2652,7 +2695,11 @@ export default function App({ container_id, imageStacks, autoCacheStack, annotat
|
||||
|
||||
}
|
||||
});
|
||||
}, [mouseToolBindings, ctrlMouseToolBindings]);
|
||||
}, [mouseToolBindings, ctrlMouseToolBindings, windowLevelMouseSensitivity]);
|
||||
|
||||
useEffect(() => {
|
||||
applyMouseToolBindings();
|
||||
}, [windowLevelMouseSensitivity, applyMouseToolBindings]);
|
||||
|
||||
const [sideMenuOpen, setSideMenuOpen] = useState(false);
|
||||
const [folderLoadMode, setFolderLoadMode] = useState<'add' | 'replace'>('add');
|
||||
@@ -3872,22 +3919,6 @@ export default function App({ container_id, imageStacks, autoCacheStack, annotat
|
||||
cancelAnimationFrame(raf);
|
||||
};
|
||||
}, [elementRef, updateOverlay, viewportGrid, viewportModes, viewportImageIds]);
|
||||
|
||||
|
||||
// Handler to reset the view (camera and window/level)
|
||||
const handleDoubleClick = useCallback((viewportIdx: number) => {
|
||||
const viewportId = `CT_${viewportIdx}`;
|
||||
const renderingEngine = renderingEngineRef.current;
|
||||
const viewport = renderingEngine.getViewport(viewportId);
|
||||
if (viewport && typeof viewport.resetCamera === "function") {
|
||||
viewport.resetCamera();
|
||||
|
||||
appLogger.debug("Restoring viewport properties for viewport", viewportIdx);
|
||||
viewport.resetProperties(); // Resets window/level and other properties
|
||||
viewport.render();
|
||||
}
|
||||
}, [])
|
||||
|
||||
// Handler to apply a preset for a specific viewport
|
||||
const handleApplyPreset = useCallback((viewportIdx: number, center: number, width: number) => {
|
||||
const viewportId = `CT_${viewportIdx}`;
|
||||
@@ -4060,6 +4091,20 @@ export default function App({ container_id, imageStacks, autoCacheStack, annotat
|
||||
});
|
||||
}, [applyDefaultWindowLevel, updateOverlay, viewportImageIds]);
|
||||
|
||||
// Handler to reset the view (camera and window/level)
|
||||
const handleDoubleClick = useCallback((viewportIdx: number) => {
|
||||
const viewportId = `CT_${viewportIdx}`;
|
||||
const renderingEngine = renderingEngineRef.current;
|
||||
const viewport = renderingEngine.getViewport(viewportId);
|
||||
if (viewport && typeof viewport.resetCamera === "function") {
|
||||
viewport.resetCamera();
|
||||
|
||||
appLogger.debug("Restoring default window/level for viewport", viewportIdx);
|
||||
applyDefaultWindowLevel(viewportIdx);
|
||||
viewport.render();
|
||||
}
|
||||
}, [applyDefaultWindowLevel])
|
||||
|
||||
const applyPresetOption = useCallback((viewportIdx: number, preset: PresetOption, persistSelection = true, resetCameraOnReset = true) => {
|
||||
if (persistSelection) {
|
||||
activePresetByViewportRef.current[viewportIdx] = preset;
|
||||
@@ -5058,7 +5103,6 @@ export default function App({ container_id, imageStacks, autoCacheStack, annotat
|
||||
RectangleROITool.toolName,
|
||||
EllipticalROITool.toolName,
|
||||
PlanarFreehandROITool.toolName,
|
||||
PlanarFreehandContourSegmentationTool.toolName,
|
||||
ProbeTool.toolName,
|
||||
SculptorTool.toolName,
|
||||
];
|
||||
@@ -5407,6 +5451,7 @@ export default function App({ container_id, imageStacks, autoCacheStack, annotat
|
||||
|| !mobileInteractionActive
|
||||
|| !mobileSingleViewportMode
|
||||
|| mobileFocusedViewport === i;
|
||||
const hasCineableStack = viewportModes[i] === "stack" && ((imageInfos[i]?.total || 0) > 1 || (imageInfos[i]?.totalFrames || 0) > 1);
|
||||
|
||||
const currentPreviewZone = dropPreview?.viewportIdx === i ? dropPreview.zone : null;
|
||||
const resolvedPreviewZone = currentPreviewZone ? resolveDropZone(currentPreviewZone) : null;
|
||||
@@ -5764,6 +5809,7 @@ export default function App({ container_id, imageStacks, autoCacheStack, annotat
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
{hasCineableStack && (
|
||||
<button
|
||||
style={{
|
||||
position: "absolute",
|
||||
@@ -5800,6 +5846,7 @@ export default function App({ container_id, imageStacks, autoCacheStack, annotat
|
||||
>
|
||||
<span aria-hidden="true">▶</span>
|
||||
</button>
|
||||
)}
|
||||
{viewportModes[i] === "stack" && annotationNavEnabled && (() => {
|
||||
// Get all annotations for this stack
|
||||
const imageIds = viewportImageIds[i];
|
||||
@@ -6166,7 +6213,7 @@ export default function App({ container_id, imageStacks, autoCacheStack, annotat
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{viewportModes[i] === "stack" && (cineControlsOpenByViewport[i] || (seriesGroupsByViewport[i] || []).length > 1 || (imageInfos[i]?.frameIndex && imageInfos[i]?.totalFrames)) && (
|
||||
{hasCineableStack && (cineControlsOpenByViewport[i] || (seriesGroupsByViewport[i] || []).length > 1 || (imageInfos[i]?.frameIndex && imageInfos[i]?.totalFrames)) && (
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
@@ -6187,7 +6234,7 @@ export default function App({ container_id, imageStacks, autoCacheStack, annotat
|
||||
}}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{cineControlsOpenByViewport[i] && imageInfos[i]?.total > 1 && (
|
||||
{cineControlsOpenByViewport[i] && hasCineableStack && (
|
||||
<>
|
||||
<button
|
||||
style={{
|
||||
@@ -6533,7 +6580,7 @@ export default function App({ container_id, imageStacks, autoCacheStack, annotat
|
||||
position: "absolute",
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: viewportModes[i] === "stack" && (cineControlsOpenByViewport[i] || (seriesGroupsByViewport[i] || []).length > 1) ? 56 : 8,
|
||||
bottom: hasCineableStack && (cineControlsOpenByViewport[i] || (seriesGroupsByViewport[i] || []).length > 1) ? 56 : 8,
|
||||
zIndex: 20,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
@@ -7851,6 +7898,20 @@ export default function App({ container_id, imageStacks, autoCacheStack, annotat
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div style={{ margin: "18px 0 8px 0", fontWeight: "bold" }}>Window/Level Mouse Sensitivity</div>
|
||||
<label style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 12 }}>
|
||||
<span style={{ minWidth: 120 }}>Drag sensitivity</span>
|
||||
<input
|
||||
type="range"
|
||||
min={0.25}
|
||||
max={2}
|
||||
step={0.05}
|
||||
value={windowLevelMouseSensitivity}
|
||||
onChange={(e) => setWindowLevelMouseSensitivity(Number(e.target.value))}
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
<span style={{ minWidth: 44, textAlign: "right" }}>{windowLevelMouseSensitivity.toFixed(2)}x</span>
|
||||
</label>
|
||||
{/* Ctrl + Mouse Button Tool Bindings */}
|
||||
<div style={{ margin: "18px 0 8px 0", fontWeight: "bold" }}>Ctrl + Mouse Button Tool Bindings</div>
|
||||
{MOUSE_BUTTONS.map((btn) => (
|
||||
@@ -8088,7 +8149,6 @@ function setupTools(MAIN_TOOL_GROUP_ID) {
|
||||
cornerstoneTools.addTool(RectangleROITool)
|
||||
cornerstoneTools.addTool(EllipticalROITool)
|
||||
cornerstoneTools.addTool(PlanarFreehandROITool)
|
||||
cornerstoneTools.addTool(PlanarFreehandContourSegmentationTool)
|
||||
cornerstoneTools.addTool(SculptorTool)
|
||||
cornerstoneTools.addTool(ReferenceCursors)
|
||||
cornerstoneTools.addTool(CrosshairsTool)
|
||||
@@ -8113,7 +8173,6 @@ function setupTools(MAIN_TOOL_GROUP_ID) {
|
||||
mainToolGroup.addTool(RectangleROITool.toolName, { calculateStats: false, getTextCallback: () => '' });
|
||||
mainToolGroup.addTool(EllipticalROITool.toolName, { getTextCallback: () => '' });
|
||||
mainToolGroup.addTool(PlanarFreehandROITool.toolName, { calculateStats: false });
|
||||
mainToolGroup.addTool(PlanarFreehandContourSegmentationTool.toolName, {});
|
||||
mainToolGroup.addTool(SculptorTool.toolName, {});
|
||||
//mainToolGroup.addTool(CrosshairsTool.toolName);
|
||||
mainToolGroup.addTool(ReferenceLinesTool.toolName);
|
||||
|
||||
Reference in New Issue
Block a user