Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 | 1027x 1027x 1027x 28995x 57x 57x 57x 57x 57x 57x 4923x 4923x 4923x 57x 57x 57x 57x 57x 57x 57x 57x 970x 970x 970x 970x 970x 970x 970x 970x 1040x 13x 1027x 1027x 1027x 1027x 1027x 1027x | import { cache, metaData, type Types } from '@cornerstonejs/core';
import { SegmentationRepresentations } from '../../../enums';
import { getSegmentation } from '../getSegmentation';
import { getLabelmaps } from '../labelmapModel';
/**
* The source (referenced) imageIds a labelmap layer applies to.
*
* Prefer an explicit `referencedImageIds` list. Otherwise derive the source
* images from each labelmap image's `referencedImageId` (a derived labelmap
* image points back at the source image it overlays). We must NOT fall back to
* the layer's own `imageIds`: those are the labelmap image ids, which never
* equal the viewport's source image ids, so the suitability check would wrongly
* fail for any layer created without an explicit `referencedImageIds` list
* (e.g. via `createAndCacheDerivedLabelmapImage`). Returns `[]` when the source
* cannot be determined yet, so callers stay permissive.
*/
function getLayerReferencedImageIds(layer): string[] {
Iif (layer.referencedImageIds?.length) {
return layer.referencedImageIds;
}
const labelmapImageIds =
layer.imageIds ??
(layer.volumeId
? (cache.getVolume(layer.volumeId) as Types.IImageVolume)?.imageIds
: undefined) ??
[];
return labelmapImageIds
.map(
(labelmapImageId) => cache.getImage(labelmapImageId)?.referencedImageId
)
.filter(Boolean) as string[];
}
/**
* The FrameOfReferenceUIDs a labelmap layer belongs to, taken from the
* labelmap volume's own metadata (derived volumes copy it from their source)
* and/or the imagePlaneModule of the source images the layer was derived from.
* Returns `[]` when none can be determined yet, so callers stay permissive.
*/
function getLayerFrameOfReferenceUIDs(layer): string[] {
const frameOfReferenceUIDs = new Set<string>();
Eif (layer.volumeId) {
const volumeFrameOfReference = (
cache.getVolume(layer.volumeId) as Types.IImageVolume
)?.metadata?.FrameOfReferenceUID;
Eif (volumeFrameOfReference) {
frameOfReferenceUIDs.add(volumeFrameOfReference);
}
}
for (const referencedImageId of getLayerReferencedImageIds(layer)) {
const imageFrameOfReference = (
metaData.get('imagePlaneModule', referencedImageId) as
| Types.ImagePlaneModule
| undefined
)?.frameOfReferenceUID;
Eif (imageFrameOfReference) {
frameOfReferenceUIDs.add(imageFrameOfReference);
}
}
return [...frameOfReferenceUIDs];
}
/**
* Volume viewports resample labelmaps geometrically, so a labelmap derived
* from a DIFFERENT series is still a legitimate destination as long as it
* shares the viewport's frame of reference (e.g. a PT-derived labelmap painted
* on CT viewports, or a SEG overlaid on a series it does not reference). Only
* a frame-of-reference mismatch is a reliable "does not belong" signal here.
*
* `getFrameOfReferenceUID` is read defensively: before `setVolumes` it is
* undefined, and a half-torn-down viewport may throw - both mean "unknown",
* so stay permissive.
*/
function volumeViewportSharesFrameOfReference(
viewport: Types.IViewport,
layers
): boolean {
let viewportFrameOfReference: string | undefined;
try {
viewportFrameOfReference = viewport.getFrameOfReferenceUID?.();
} catch (error) {
return true;
}
Iif (!viewportFrameOfReference) {
return true;
}
const labelmapFrameOfReferenceUIDs = layers.flatMap((layer) =>
getLayerFrameOfReferenceUIDs(layer)
);
Iif (!labelmapFrameOfReferenceUIDs.length) {
return true;
}
return labelmapFrameOfReferenceUIDs.includes(viewportFrameOfReference);
}
/**
* Stack viewports render a labelmap by mapping each displayed imageId to the
* labelmap image derived from it, so a stack that displays none of the
* labelmap's source images genuinely cannot mount it - rendering it there only
* disturbs the viewport (blanking it in the shared rendering engine, or
* corrupting its image-index overlay). Hence the strict imageId-intersection
* test. `getImageIds` is wrapped so a not-yet-initialized viewport reads as
* "ids unknown" and stays permissive.
*/
function stackViewportReferencesImages(
viewport: Types.IViewport,
layers
): boolean {
const referencedImageIds = layers.flatMap((layer) =>
getLayerReferencedImageIds(layer)
);
Iif (!referencedImageIds.length) {
return true;
}
let viewportImageIds: string[];
try {
viewportImageIds =
(viewport as { getImageIds?: () => string[] }).getImageIds?.() ?? [];
} catch (error) {
return true;
}
Iif (!viewportImageIds.length) {
return true;
}
const referencedImageIdSet = new Set(referencedImageIds);
return viewportImageIds.some((imageId) => referencedImageIdSet.has(imageId));
}
/**
* The single place that decides whether a segmentation overlay
* (representation) is compatible with a viewport, i.e. whether the viewport is
* a suitable destination for it. Any future compatibility rule - new
* representation types, geometry checks, orientation constraints - belongs
* here, so every caller (representation add, actor sync, ...) picks it up.
*
* Current rules:
* - Non-labelmap representations (contour, surface, or an omitted type, which
* the state manager also treats as non-labelmap) have no viewport
* constraints and are always compatible.
* - Labelmap on a stack viewport: the stack must actually display one of the
* labelmap's source images (see `stackViewportReferencesImages`); a stack
* showing an unrelated series cannot mount the labelmap and would only be
* disturbed by it.
* - Labelmap on a volume viewport (including fusion): volume viewports
* resample labelmaps by geometry, so any labelmap sharing the viewport's
* FrameOfReferenceUID is compatible - even one derived from a series the
* viewport does not display (data-overlay and cross-series painting
* workflows rely on this). Only a differing frame of reference suppresses.
*
* Returns `true` whenever compatibility cannot be determined (unknown
* segmentation, no source images or frame of reference derivable yet, viewport
* not fully set up) so callers never wrongly suppress a representation that may
* legitimately belong.
*/
export function isSegmentationOverlayCompatible(
viewport: Types.IViewport | undefined,
segmentationId: string,
representationType: SegmentationRepresentations | undefined
): boolean {
if (representationType !== SegmentationRepresentations.Labelmap) {
return true;
}
Iif (!viewport) {
return true;
}
const segmentation = getSegmentation(segmentationId);
Iif (!segmentation) {
return true;
}
const layers = getLabelmaps(segmentation);
// Only BaseVolumeViewport exposes getAllVolumeIds, so its presence
// discriminates volume from stack (and other image-based) viewports.
const isVolumeViewport =
typeof (viewport as { getAllVolumeIds?: () => string[] })
.getAllVolumeIds === 'function';
return isVolumeViewport
? volumeViewportSharesFrameOfReference(viewport, layers)
: stackViewportReferencesImages(viewport, layers);
}
|