Skip to content

Commit

Permalink
feat(CurrentImageProvider): inject current image
Browse files Browse the repository at this point in the history
Decouples the current image from the current selection.
  • Loading branch information
floryst committed Feb 6, 2024
1 parent 6f3685d commit b069b68
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 67 deletions.
19 changes: 19 additions & 0 deletions src/components/CurrentImageProvider.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<script setup lang="ts">
import { Maybe } from '@/src/types';
import { provide, toRefs } from 'vue';
import { CurrentImageInjectionKey } from '@/src/composables/useCurrentImage';
const props = defineProps<{
imageId: Maybe<string>;
}>();
const { imageId: imageID } = toRefs(props);
provide(CurrentImageInjectionKey, {
imageID,
});
</script>

<template>
<slot />
</template>
2 changes: 1 addition & 1 deletion src/components/VtkTwoView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -863,7 +863,7 @@ export default defineComponent({
.filter(
({ tool }) =>
tool.slice === currentSlice.value &&
doesToolFrameMatchViewAxis(viewAxis, tool)
doesToolFrameMatchViewAxis(viewAxis, tool, curImageMetadata)
)
.flatMap(({ store, tool }) => store.getPoints(tool.id));
});
Expand Down
31 changes: 21 additions & 10 deletions src/composables/annotationTool.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
import { Ref, UnwrapRef, computed, readonly, ref, watch } from 'vue';
import {
MaybeRef,
Ref,
UnwrapRef,
computed,
readonly,
ref,
unref,
watch,
} from 'vue';
import { Vector2 } from '@kitware/vtk.js/types';
import { useCurrentImage } from '@/src/composables/useCurrentImage';
import { frameOfReferenceToImageSliceAndAxis } from '@/src/utils/frameOfReference';
Expand All @@ -19,34 +28,35 @@ import {
ContextMenuEvent,
vtkAnnotationToolWidget,
} from '@/src/vtk/ToolWidgetUtils/types';
import { ImageMetadata } from '@/src/types/image';

const SHOW_OVERLAY_DELAY = 250; // milliseconds

// does the tools's frame of reference match
// the view's axis
export const doesToolFrameMatchViewAxis = <Tool extends AnnotationTool>(
viewAxis: Ref<LPSAxis>,
tool: Partial<Tool>
viewAxis: MaybeRef<LPSAxis>,
tool: Partial<Tool>,
imageMetadata: MaybeRef<ImageMetadata>
) => {
if (!tool.frameOfReference) return false;

const { currentImageMetadata } = useCurrentImage();
const toolAxis = frameOfReferenceToImageSliceAndAxis(
tool.frameOfReference,
currentImageMetadata.value,
unref(imageMetadata),
{
allowOutOfBoundsSlice: true,
}
);
return !!toolAxis && toolAxis.axis === viewAxis.value;
return !!toolAxis && toolAxis.axis === unref(viewAxis);
};

export const useCurrentTools = <S extends AnnotationToolStore>(
toolStore: S,
viewAxis: Ref<LPSAxis>
) =>
computed(() => {
const { currentImageID } = useCurrentImage();
) => {
const { currentImageID, currentImageMetadata } = useCurrentImage();
return computed(() => {
const curImageID = currentImageID.value;

type ToolType = S['tools'][number];
Expand All @@ -55,11 +65,12 @@ export const useCurrentTools = <S extends AnnotationToolStore>(
// current view axis and not hidden
return (
tool.imageID === curImageID &&
doesToolFrameMatchViewAxis(viewAxis, tool) &&
doesToolFrameMatchViewAxis(viewAxis, tool, currentImageMetadata) &&
!tool.hidden
);
});
});
};

// --- Context Menu --- //

Expand Down
127 changes: 72 additions & 55 deletions src/composables/useCurrentImage.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,32 @@
import { computed } from 'vue';
import { useDatasetStore } from '../store/datasets';
import { useDICOMStore } from '../store/datasets-dicom';
import { defaultImageMetadata, useImageStore } from '../store/datasets-images';
import { useLayersStore } from '../store/datasets-layers';
import { createLPSBounds, getAxisBounds } from '../utils/lps';
import {
InjectionKey,
MaybeRef,
Ref,
computed,
hasInjectionContext,
inject,
unref,
} from 'vue';
import { Maybe } from '@/src/types';
import {
defaultImageMetadata,
useImageStore,
} from '@/src/store/datasets-images';
import { useLayersStore } from '@/src/store/datasets-layers';
import { createLPSBounds, getAxisBounds } from '@/src/utils/lps';
import { getImageID, useDatasetStore } from '@/src/store/datasets';
import { storeToRefs } from 'pinia';

export interface CurrentImageContext {
imageID: Ref<Maybe<string>>;
}

export const CurrentImageInjectionKey = Symbol(
'CurrentImage'
) as InjectionKey<CurrentImageContext>;

// Returns a spatially inflated image extent
export function getImageSpatialExtent(imageID: string | null) {
export function getImageSpatialExtent(imageID: Maybe<string>) {
const imageStore = useImageStore();

if (imageID && imageID in imageStore.metadata) {
Expand All @@ -24,62 +44,59 @@ export function getImageSpatialExtent(imageID: string | null) {
return createLPSBounds();
}

export function useCurrentImage() {
const dataStore = useDatasetStore();
const dicomStore = useDICOMStore();
const imageStore = useImageStore();
const layersStore = useLayersStore();

const currentImageID = computed(() => {
const { primarySelection } = dataStore;
const { volumeToImageID } = dicomStore;
export function getImageMetadata(imageID: Maybe<string>) {
const { metadata } = useImageStore();
return imageID ? metadata[imageID] : defaultImageMetadata();
}

if (primarySelection?.type === 'image') {
return primarySelection.dataID;
}
if (primarySelection?.type === 'dicom') {
return volumeToImageID[primarySelection.volumeKey] || null;
}
return null;
});
export function getImageData(imageID: Maybe<string>) {
const { dataIndex } = useImageStore();
return imageID ? dataIndex[imageID] : null;
}

const currentImageMetadata = computed(() => {
const { metadata } = imageStore;
const imageID = currentImageID.value;
export function getIsImageLoading(imageID: Maybe<string>) {
const dataStore = useDatasetStore();
if (!dataStore.primarySelection) return false;

if (imageID) {
return metadata[imageID];
}
return defaultImageMetadata();
});
const selectedImageID = getImageID(dataStore.primarySelection);
if (selectedImageID !== unref(imageID)) return false;

const currentImageData = computed(() => {
if (currentImageID.value)
// assumed to be only images for now
return imageStore.dataIndex[currentImageID.value];
return undefined;
});
return !!dataStore.primarySelection && !dataStore.primaryDataset;
}

const currentImageExtent = computed(() =>
getImageSpatialExtent(currentImageID.value)
);
export function getImageLayers(imageID: Maybe<string>) {
const layersStore = useLayersStore();
return layersStore
.getLayers(imageID ? { type: 'image', dataID: imageID } : null)
.filter(({ id }) => id in layersStore.layerImages);
}

const isImageLoading = computed(() => {
return !!dataStore.primarySelection && !dataStore.primaryDataset;
});
export function useImage(imageID: MaybeRef<Maybe<string>>) {
return {
id: computed(() => unref(imageID)),
imageData: computed(() => getImageData(unref(imageID))),
metadata: computed(() => getImageMetadata(unref(imageID))),
extent: computed(() => getImageSpatialExtent(unref(imageID))),
isLoading: computed(() => getIsImageLoading(unref(imageID))),
layers: computed(() => getImageLayers(unref(imageID))),
};
}

const currentLayers = computed(() =>
layersStore
.getLayers(dataStore.primarySelection)
.filter(({ id }) => id in layersStore.layerImages)
);
export function useCurrentImage() {
const { primaryImageID } = storeToRefs(useDatasetStore());
const defaultContext = { imageID: primaryImageID };
const { imageID } = hasInjectionContext()
? inject(CurrentImageInjectionKey, defaultContext)
: defaultContext;

const { id, imageData, metadata, extent, isLoading, layers } =
useImage(imageID);
return {
currentImageData,
currentImageID,
currentImageMetadata,
currentImageExtent,
isImageLoading,
currentLayers,
currentImageID: id,
currentImageMetadata: metadata,
currentImageData: imageData,
currentImageExtent: extent,
isImageLoading: isLoading,
currentLayers: layers,
};
}
1 change: 1 addition & 0 deletions src/store/datasets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ export const useDatasetStore = defineStore('dataset', () => {
});

return {
primaryImageID,
primarySelection,
primaryDataset,
allDataIDs,
Expand Down
3 changes: 2 additions & 1 deletion src/store/tools/useAnnotationTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,10 @@ export const useAnnotationTool = <
});
});

const { currentImageID, currentImageMetadata } = useCurrentImage();

function jumpToTool(toolID: ToolID) {
const tool = toolByID.value[toolID];
const { currentImageID, currentImageMetadata } = useCurrentImage();

const imageID = currentImageID.value;
if (!imageID || tool.imageID !== imageID) return;
Expand Down

0 comments on commit b069b68

Please sign in to comment.