numerous more fixes

This commit is contained in:
Ross
2025-06-02 12:37:01 +01:00
parent 43c3f75889
commit 2518da4413
5 changed files with 162 additions and 75 deletions
+79 -60
View File
@@ -1,5 +1,4 @@
import { useEffect, useRef, useCallback, useState } from "react"
import { createImageIds, availableImageIds } from "./lib/createImageIds.ts"
import {
RenderingEngine,
Enums,
@@ -65,7 +64,6 @@ const { MouseBindings, KeyboardBindings } = csToolsEnums;
const { IMAGE_RENDERED } = Enums.Events;
const MAIN_TOOL_GROUP_ID = 'MAIN_TOOL_GROUP_ID';
// Common CT presets (add more as needed)
const WINDOW_LEVEL_PRESETS = [
@@ -115,9 +113,14 @@ const ALL_MOUSE_BINDINGS = [
{ label: "Mouse Wheel + Left", value: "Wheel_Primary", mask: 524289 },
];
type AppProps = {
container_id?: string;
imageStacks: () => Promise<string[][]>;
// ...other props if needed
};
// App definintion
function App() {
function App({ container_id, imageStacks, ...props }: AppProps) {
const elementRef = useRef<HTMLDivElement>(null)
const running = useRef(false)
@@ -130,7 +133,8 @@ function App() {
const [openDropdownIdx, setOpenDropdownIdx] = useState<number | null>(null);
const MAX_VIEWPORTS = 9;
const renderingEngineId = "mainRenderingEngine";
const renderingEngineId = "renderingEngine_" + container_id;
const MAIN_TOOL_GROUP_ID = "TOOL_GROUP_" + container_id;
const renderingEngineRef = useRef<any>(null);
const restoringStateRef = useRef(false);
@@ -148,7 +152,7 @@ function App() {
// Load available stacks on mount
useEffect(() => {
availableImageIds().then(async stacks => {
imageStacks().then(async stacks => {
// Try to extract StudyInstanceUID for each stack
const stackEntries: StackEntry[] = await Promise.all(
stacks.map(async imageIds => {
@@ -663,7 +667,7 @@ function App() {
// --- In your setup function, use the correct tool group per viewport ---
let mainToolGroup = ToolGroupManager.getToolGroup(MAIN_TOOL_GROUP_ID);
if (!mainToolGroup) {
mainToolGroup = setupTools().mainToolGroup;
mainToolGroup = setupTools(MAIN_TOOL_GROUP_ID).mainToolGroup;
}
@@ -1065,8 +1069,9 @@ function App() {
}
}
useEffect(() => {
const apiKey = container_id || "default";
// Export the current viewer state as JSON
window.exportViewerState = () => {
window[`exportViewerState_${apiKey}`] = () => {
const numActive = viewportGrid.rows * viewportGrid.cols;
const modes = viewportModes.slice(0, numActive);
const stackIdx = selectedStackIdx.slice(0, numActive);
@@ -1117,7 +1122,7 @@ function App() {
};
// Import and restore a viewer state from JSON (using stack indices)
window.importViewerState = (json: string) => {
window[`importViewerState_${apiKey}`] = (json: string) => {
try {
restoringStateRef.current = true;
const state = typeof json === "string" ? JSON.parse(json) : json;
@@ -1189,7 +1194,7 @@ function App() {
};
// Export all annotations using cornerstoneTools.annotation.state.getAllAnnotations()
window.exportAnnotations = () => {
window[`exportAnnotations_${apiKey}`] = () => {
try {
let allAnnotations = cornerstoneTools.annotation.state.getAllAnnotations();
console.log("Exporting annotations:", allAnnotations);
@@ -1221,7 +1226,7 @@ function App() {
};
// Import and restore all annotations using cornerstoneTools.annotation.state.restoreAnnotations()
window.importAnnotations = (json: string) => {
window[`importAnnotations_${apiKey}`] = (json: string) => {
try {
const annotationData = typeof json === "string" ? JSON.parse(json) : json;
// Remove all existing annotations if needed
@@ -1246,10 +1251,10 @@ function App() {
// Cleanup
return () => {
window.exportViewerState = undefined;
window.importViewerState = undefined;
window.exportAnnotations = undefined;
window.importAnnotations = undefined;
window[`exportViewerState_${apiKey}`] = undefined;
window[`importViewerState_${apiKey}`] = undefined;
window[`exportAnnotations_${apiKey}`] = undefined;
window[`importAnnotations_${apiKey}`] = undefined;
};
}, [
viewportGrid,
@@ -1271,8 +1276,10 @@ function App() {
const referenceStackObj = availableStacks[referenceStackIdx];
const referenceUID = referenceStackObj?.studyInstanceUID;
const numVisible = viewportGrid.rows * viewportGrid.cols;
const viewports = [];
for (let i = 0; i < MAX_VIEWPORTS; i++) {
//for (let i = 0; i < MAX_VIEWPORTS; i++) {
for (let i = 0; i < numVisible; i++) {
// Determine if this viewport should be visible in the current grid
const isVisible = i < viewportGrid.rows * viewportGrid.cols;
@@ -1282,7 +1289,7 @@ function App() {
ref={el => (viewportRefs.current[i] = el)}
style={{
flex: 1,
border: "1px solid #222",
border: isVisible ? "1px solid #222" : "none", // Only show border if visible
background: "#000",
minWidth: 0,
minHeight: 0,
@@ -1292,8 +1299,8 @@ function App() {
gridRow: Math.floor(i / viewportGrid.cols) + 1,
gridColumn: (i % viewportGrid.cols) + 1,
height: "100%",
visibility: isVisible ? "visible" : "hidden",
pointerEvents: isVisible ? "auto" : "none",
//visibility: isVisible ? "visible" : "hidden",
//pointerEvents: isVisible ? "auto" : "none",
// Remove outline, use only boxShadow for indicator
boxShadow: activeViewport === i
? "0 0 0 4px #1976d2, 0 0 16px 4px #1976d2cc"
@@ -1676,20 +1683,23 @@ function App() {
</div>
</div>
)}
{isVisible && (
<div
style={{
position: "absolute",
left: 8,
right: 8,
left: 0,
right: 0,
bottom: 8,
zIndex: 20,
display: "flex",
alignItems: "center",
justifyContent: "flex-end",
gap: "8px",
background: "rgba(34,34,34,0.85)",
borderRadius: "4px",
padding: "2px 8px",
justifyContent: "center", // Center horizontally
pointerEvents: "auto",
width: "100%",
// Remove background/gap if you want only the selector to be visible
background: "none",
borderRadius: "0",
padding: 0,
}}
>
{/* Custom Stack Dropdown */}
@@ -1787,6 +1797,7 @@ function App() {
const thisUID = stackObj.studyInstanceUID;
const sameStudy = referenceUID && thisUID && referenceUID === thisUID;
const dotColor = sameStudy ? "#4caf50" : "#888";
const isEmpty = stackObj.imageIds.length === 0;
return (
<div
key={idx}
@@ -1795,19 +1806,22 @@ function App() {
style={{
padding: "6px 12px",
background: selectedStackIdx[i] === idx ? "#444" : "#222",
color: "#fff",
cursor: "pointer",
color: isEmpty ? "#aaa" : "#fff",
cursor: isEmpty ? "not-allowed" : "pointer",
display: "flex",
alignItems: "center",
fontWeight: selectedStackIdx[i] === idx ? "bold" : "normal",
borderBottom: "1px solid #333",
userSelect: "none",
opacity: isEmpty ? 0.6 : 1,
}}
tabIndex={-1}
onMouseDown={e => {
e.preventDefault();
handleLoadStack(i, idx);
setOpenDropdownIdx(null);
if (!isEmpty) {
handleLoadStack(i, idx);
setOpenDropdownIdx(null);
}
}}
>
<span style={{
@@ -1817,7 +1831,8 @@ function App() {
fontSize: "16px",
verticalAlign: "middle",
}}></span>
Stack {idx + 1} ({stackObj.imageIds.length} images)
Stack {idx + 1} ({stackObj.imageIds.length} images
{isEmpty && <span style={{ color: "#f44336", marginLeft: 6 }}>(empty)</span>})
</div>
);
})}
@@ -1825,6 +1840,7 @@ function App() {
)}
</div>
</div>
)}
</div>
);
}
@@ -1833,16 +1849,17 @@ function App() {
return (
<div style={{
position: "relative",
width: "99vw",
height: "100%", // <-- changed from 500px
maxHeight: "100%", // <-- ensures it doesn't overflow parent
padding: "12px", // <-- Add this line
boxSizing: "border-box", // <-- Ensure padding doesn't shrink content
background: "#222",
// overflow: "hidden",
}}>
// App root
<div style={{
position: "absolute",
inset: 0,
width: "100%",
height: "100%",
boxSizing: "border-box",
background: "#222",
padding: "12px",
overflow: "hidden",
}}>
<div>
{/* Side menu toggle button */}
<button
@@ -1871,25 +1888,24 @@ function App() {
</button>
{/* Side menu drawer */}
<div
style={{
position: "absolute",
top: 0,
left: sideMenuOpen ? 0 : -260,
width: 220,
height: "100%",
background: "#222",
color: "#fff",
zIndex: 199,
boxShadow: sideMenuOpen ? "2px 0 12px rgba(0,0,0,0.3)" : "none",
transition: "left 0.25s cubic-bezier(.4,2,.6,1)",
flexDirection: "column",
padding: "24px 16px 16px 16px",
display: "flex",
overflow: "hidden", // <-- add this
pointerEvents: sideMenuOpen ? "auto" : "none", // <-- add this
}}
>
{sideMenuOpen && (
<div
style={{
position: "absolute",
top: 0,
left: 0,
width: 220,
height: "100%",
background: "#222",
color: "#fff",
zIndex: 199,
boxShadow: "2px 0 12px rgba(0,0,0,0.3)",
flexDirection: "column",
padding: "24px 16px 16px 16px",
display: "flex",
overflow: "hidden",
}}
>
<div
style={{
flex: 1,
@@ -2040,6 +2056,7 @@ function App() {
</div>
</div>
)}
</div>
{/* Viewport grid menu at top center */}
@@ -2096,6 +2113,7 @@ function App() {
boxShadow: "0 2px 8px rgba(0,0,0,0.2)",
pointerEvents: gridBtnActive ? "auto" : "none", // Only clickable when active
zIndex: 2,
display: gridBtnActive ? "block" : "none",
}}
onMouseLeave={() => setGridBtnActive(false)}
onBlur={() => setGridBtnActive(false)}
@@ -2452,7 +2470,8 @@ function App() {
export default App
function setupTools() {
function setupTools(MAIN_TOOL_GROUP_ID) {
console.log('Setting up tools...', MAIN_TOOL_GROUP_ID);
cornerstoneTools.addTool(PanTool)
cornerstoneTools.addTool(WindowLevelTool)
cornerstoneTools.addTool(StackScrollTool)