add simple annotation navigation
This commit is contained in:
@@ -1455,6 +1455,7 @@ function App({ container_id, imageStacks, autoCacheStack, ...props }: AppProps)
|
||||
const numVisible = viewportGrid.rows * viewportGrid.cols;
|
||||
const viewports = [];
|
||||
//for (let i = 0; i < MAX_VIEWPORTS; i++) {
|
||||
// Viewports rendering loop
|
||||
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;
|
||||
@@ -1640,6 +1641,103 @@ function App({ container_id, imageStacks, autoCacheStack, ...props }: AppProps)
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
// Annotations buttons
|
||||
{viewportModes[i] === "stack" && (() => {
|
||||
// Get all annotations for this stack
|
||||
const imageIds = viewportImageIds[i];
|
||||
const allAnnotations = cornerstoneTools.annotation.state.getAllAnnotations() || [];
|
||||
const stackAnnotations = allAnnotations.filter(
|
||||
ann =>
|
||||
ann.metadata &&
|
||||
ann.metadata.referencedImageId &&
|
||||
imageIds.includes(ann.metadata.referencedImageId)
|
||||
);
|
||||
if (!stackAnnotations.length) return null;
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
left: "50%",
|
||||
bottom: 60,
|
||||
transform: "translateX(-50%)",
|
||||
zIndex: 30,
|
||||
display: "flex",
|
||||
gap: 4,
|
||||
pointerEvents: "auto",
|
||||
}}
|
||||
>
|
||||
<button
|
||||
style={{
|
||||
background: "rgba(30,30,30,0.7)",
|
||||
color: "#fff",
|
||||
border: "none",
|
||||
borderRadius: "50%",
|
||||
width: 28,
|
||||
height: 28,
|
||||
fontSize: "16px",
|
||||
fontWeight: "bold",
|
||||
cursor: "pointer",
|
||||
opacity: 0.7,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
boxShadow: "0 1px 4px rgba(0,0,0,0.18)",
|
||||
transition: "opacity 0.2s",
|
||||
}}
|
||||
title="Previous Annotation"
|
||||
aria-label="Previous Annotation"
|
||||
onClick={() => {
|
||||
const renderingEngine = renderingEngineRef.current;
|
||||
const viewport = renderingEngine?.getViewport(`CT_${i}`);
|
||||
if (!viewport || typeof viewport.getCurrentImageIdIndex !== "function") return;
|
||||
const currentIdx = viewport.getCurrentImageIdIndex();
|
||||
const prevIdx = findNextAnnotationIdx(currentIdx, imageIds, stackAnnotations, -1);
|
||||
if (prevIdx !== null && typeof viewport.setImageIdIndex === "function") {
|
||||
csUtilities.jumpToSlice(viewport.element, { imageIndex: prevIdx });
|
||||
updateOverlay(i, viewport);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<span aria-hidden="true">◀</span>
|
||||
</button>
|
||||
<button
|
||||
style={{
|
||||
background: "rgba(30,30,30,0.7)",
|
||||
color: "#fff",
|
||||
border: "none",
|
||||
borderRadius: "50%",
|
||||
width: 28,
|
||||
height: 28,
|
||||
fontSize: "16px",
|
||||
fontWeight: "bold",
|
||||
cursor: "pointer",
|
||||
opacity: 0.7,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
boxShadow: "0 1px 4px rgba(0,0,0,0.18)",
|
||||
transition: "opacity 0.2s",
|
||||
}}
|
||||
title="Next Annotation"
|
||||
aria-label="Next Annotation"
|
||||
onClick={() => {
|
||||
const renderingEngine = renderingEngineRef.current;
|
||||
const viewport = renderingEngine?.getViewport(`CT_${i}`);
|
||||
if (!viewport || typeof viewport.getCurrentImageIdIndex !== "function") return;
|
||||
const currentIdx = viewport.getCurrentImageIdIndex();
|
||||
const nextIdx = findNextAnnotationIdx(currentIdx, imageIds, stackAnnotations, 1);
|
||||
if (nextIdx !== null && typeof viewport.setImageIdIndex === "function") {
|
||||
csUtilities.jumpToSlice(viewport.element, { imageIndex: nextIdx });
|
||||
updateOverlay(i, viewport);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<span aria-hidden="true">▶</span>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
})()}
|
||||
// ...existing code...
|
||||
{/* Overlay info (bottom left of viewport) */}
|
||||
<div
|
||||
style={{
|
||||
@@ -2693,3 +2791,30 @@ function setupTools(MAIN_TOOL_GROUP_ID) {
|
||||
|
||||
return mainToolGroup;
|
||||
}
|
||||
|
||||
// Add this helper to find the next/previous annotation index for the current viewport
|
||||
function findNextAnnotationIdx(currentIdx: number, imageIds: string[], annotations: any[], direction: 1 | -1) {
|
||||
if (!annotations.length || !imageIds.length) return null;
|
||||
// Find all annotation indices in this stack
|
||||
const annIndices = annotations
|
||||
.map(ann => imageIds.findIndex(id => id === ann.metadata.referencedImageId))
|
||||
.filter(idx => idx !== -1)
|
||||
.sort((a, b) => a - b);
|
||||
|
||||
if (!annIndices.length) return null;
|
||||
|
||||
// Find the next/prev annotation index
|
||||
if (direction === 1) {
|
||||
// Next
|
||||
for (let idx of annIndices) {
|
||||
if (idx > currentIdx) return idx;
|
||||
}
|
||||
return annIndices[0]; // wrap around
|
||||
} else {
|
||||
// Previous
|
||||
for (let i = annIndices.length - 1; i >= 0; i--) {
|
||||
if (annIndices[i] < currentIdx) return annIndices[i];
|
||||
}
|
||||
return annIndices[annIndices.length - 1]; // wrap around
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user