@cornerstonejs/core
Version:
Cornerstone3D Core
261 lines (260 loc) • 9.23 kB
JavaScript
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;
}