UNPKG

@cornerstonejs/core

Version:
261 lines (260 loc) • 9.23 kB
import { vec3 } from 'gl-matrix'; import imageIdToURI from '../../utilities/imageIdToURI.js'; import isEqual from '../../utilities/isEqual.js'; export function isGenericViewportReferenceViewable(viewRef, contexts, options = {}) { if (!viewRef) { return false; } return contexts.some((context) => isReferenceViewableInContext(viewRef, context, options)); } export function getDimensionGroupReferenceContext(value) { const candidate = value; const dimensionGroupNumber = toPositiveInteger(candidate?.dimensionGroupNumber); const numDimensionGroups = toPositiveInteger(candidate?.numDimensionGroups); return { dimensionGroupNumber, numDimensionGroups, }; } function isReferenceViewableInContext(viewRef, context, options) { if (!isFrameOfReferenceCompatible(viewRef, context)) { return false; } if (!isDataIdCompatible(viewRef, context)) { return false; } if (!isVolumeIdCompatible(viewRef, context)) { return false; } if (!isPlaneCompatible(viewRef, context, options)) { return false; } if (!isReferencedImageCompatible(viewRef, context, options)) { return false; } if (!isDimensionGroupCompatible(viewRef, context, options)) { return false; } return isSliceIndexCompatible(viewRef, context, options); } function isFrameOfReferenceCompatible(viewRef, context) { return (!viewRef.FrameOfReferenceUID || viewRef.FrameOfReferenceUID === context.frameOfReferenceUID); } function isDataIdCompatible(viewRef, context) { if (!viewRef.dataId) { return true; } return getContextDataIds(context).includes(viewRef.dataId); } function isVolumeIdCompatible(viewRef, context) { if (!viewRef.volumeId) { return true; } return getContextVolumeIds(context).includes(viewRef.volumeId); } function isPlaneCompatible(viewRef, context, options) { if (viewRef.planeRestriction) { return isPlaneRestrictionViewable(viewRef.planeRestriction, context, options); } if (!viewRef.viewPlaneNormal) { return true; } const { viewPlaneNormal } = context; if (!viewPlaneNormal) { return false; } return (isEqual(viewRef.viewPlaneNormal, viewPlaneNormal) || isEqual(vec3.negate(vec3.create(), viewPlaneNormal), viewRef.viewPlaneNormal) || Boolean(options.withOrientation)); } function isPlaneRestrictionViewable(planeRestriction, context, options) { if (planeRestriction.FrameOfReferenceUID !== context.frameOfReferenceUID) { return false; } if (options.withOrientation) { return true; } const { cameraFocalPoint, viewPlaneNormal } = context; if (!cameraFocalPoint || !viewPlaneNormal) { return false; } if (planeRestriction.inPlaneVector1 && !isEqual(0, vec3.dot(viewPlaneNormal, planeRestriction.inPlaneVector1))) { return false; } if (planeRestriction.inPlaneVector2 && !isEqual(0, vec3.dot(viewPlaneNormal, planeRestriction.inPlaneVector2))) { return false; } if (options.withNavigation) { return true; } const pointVector = vec3.sub(vec3.create(), planeRestriction.point, cameraFocalPoint); return isEqual(0, vec3.dot(pointVector, viewPlaneNormal)); } function isReferencedImageCompatible(viewRef, context, options) { if (viewRef.planeRestriction && viewRef.volumeId && getContextVolumeIds(context).includes(viewRef.volumeId)) { return true; } const referencedImageURIs = getReferencedImageURIs(viewRef); if (!referencedImageURIs.length) { return true; } const imageURIEntries = getContextImageURIEntries(context); if (!imageURIEntries.length) { return false; } const currentImageIdIndex = context.currentImageIdIndex ?? 0; const currentImageURIEntry = imageURIEntries[currentImageIdIndex]; if (imageURIEntryMatches(currentImageURIEntry, referencedImageURIs)) { return true; } const foundSliceIndex = imageURIEntries.findIndex((imageURIEntry) => imageURIEntryMatches(imageURIEntry, referencedImageURIs)); if (foundSliceIndex === -1) { return false; } if (context.allowAnyImageReference) { return true; } if (options.withNavigation) { return true; } const rangeEndSliceIndex = getReferencedImageRangeEndIndex(imageURIEntries, viewRef); if (rangeEndSliceIndex !== undefined) { const start = Math.min(foundSliceIndex, rangeEndSliceIndex); const end = Math.max(foundSliceIndex, rangeEndSliceIndex); return start <= currentImageIdIndex && currentImageIdIndex <= end; } return currentImageIdIndex === foundSliceIndex; } function isDimensionGroupCompatible(viewRef, context, options) { const hasDimensionGroupRestriction = typeof viewRef.dimensionGroupNumber !== 'undefined'; const dimensionGroupNumber = toPositiveInteger(viewRef.dimensionGroupNumber); if (!hasDimensionGroupRestriction) { return true; } if (!dimensionGroupNumber) { return false; } if (context.dimensionGroupNumber === dimensionGroupNumber) { return true; } if (!options.withNavigation) { return false; } return (typeof context.numDimensionGroups === 'number' && dimensionGroupNumber <= context.numDimensionGroups); } function isSliceIndexCompatible(viewRef, context, options) { const sliceIndex = viewRef.sliceIndex; if (sliceIndex === undefined) { return true; } const { currentImageIdIndex } = context; if (Array.isArray(sliceIndex)) { if (typeof currentImageIdIndex === 'number') { return (sliceIndex[0] <= currentImageIdIndex && currentImageIdIndex <= sliceIndex[1]); } return false; } if (!Number.isFinite(sliceIndex)) { return false; } if (currentImageIdIndex === sliceIndex) { return true; } if (!options.withNavigation) { return false; } return isIndexInImageRange(sliceIndex, context.imageIds); } function getReferencedImageURIs(viewRef) { return uniqueStrings([ viewRef.referencedImageURI, ...(viewRef.referencedImageId ? getImageIdCompatibleURIs(viewRef.referencedImageId) : []), ]); } function getReferencedImageRangeEndIndex(imageURIEntries, viewRef) { const rangeEndImageURI = viewRef.multiSliceReference && getReferencedImageURIs(viewRef.multiSliceReference); if (!rangeEndImageURI?.length) { return; } const rangeEndSliceIndex = imageURIEntries.findIndex((imageURIEntry) => imageURIEntryMatches(imageURIEntry, rangeEndImageURI)); return rangeEndSliceIndex === -1 ? undefined : rangeEndSliceIndex; } const imageIdCompatibleURIsCache = new Map(); const imageIdsURIEntriesCache = new WeakMap(); function getImageIdsURIEntries(imageIds) { let entries = imageIdsURIEntriesCache.get(imageIds); if (!entries) { entries = imageIds.map((imageId) => getImageIdCompatibleURIs(imageId)); imageIdsURIEntriesCache.set(imageIds, entries); } return entries; } function getContextImageURIEntries(context) { const baseEntries = context.imageIds?.length ? getImageIdsURIEntries(context.imageIds) : []; const extraImageURIs = context.imageURIs; if (!extraImageURIs?.length) { return baseEntries; } const imageURIEntries = baseEntries.slice(); const knownImageURIs = new Set(); for (const imageURIEntry of baseEntries) { for (const imageURI of imageURIEntry) { knownImageURIs.add(imageURI); } } for (const imageURI of extraImageURIs) { if (!knownImageURIs.has(imageURI)) { imageURIEntries.push([imageURI]); knownImageURIs.add(imageURI); } } return imageURIEntries; } function getImageIdCompatibleURIs(imageId) { let uris = imageIdCompatibleURIsCache.get(imageId); if (!uris) { uris = uniqueStrings([imageIdToURI(imageId), getLegacyImageIdURI(imageId)]); imageIdCompatibleURIsCache.set(imageId, uris); } return uris; } function getLegacyImageIdURI(imageId) { const colonIndex = imageId.indexOf(':'); return colonIndex === -1 ? imageId : imageId.substring(colonIndex + 1); } function imageURIEntryMatches(imageURIEntry, referencedImageURIs) { return Boolean(imageURIEntry?.some((imageURI) => referencedImageURIs.includes(imageURI))); } function getContextDataIds(context) { return uniqueStrings([context.dataId, ...(context.dataIds || [])]); } function getContextVolumeIds(context) { return uniqueStrings([context.volumeId, ...(context.volumeIds || [])]); } function isIndexInImageRange(index, imageIds) { if (!imageIds?.length) { return true; } return index >= 0 && index < imageIds.length; } function uniqueStrings(values) { return Array.from(new Set(values.filter((value) => typeof value === 'string'))); } function toPositiveInteger(value) { if (typeof value !== 'number' || !Number.isInteger(value) || value < 1) { return; } return value; }