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 Sep 12, 2023
1 parent 6ab4afb commit f348023
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 62 deletions.
31 changes: 31 additions & 0 deletions src/components/CurrentImageProvider.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<script setup lang="ts">
import { Maybe } from '@/src/types';
import { computed, provide, toRefs } from 'vue';
import {
CurrentImageInjectionKey,
getImageMetadata,
getImageData,
getImageLayers,
getImageSpatialExtent,
getIsImageLoading,
} from '@/src/composables/useCurrentImage';
const props = defineProps<{
imageId: Maybe<string>;
}>();
const { imageId } = toRefs(props);
provide(CurrentImageInjectionKey, {
id: imageId,
metadata: computed(() => getImageMetadata(imageId.value)),
imageData: computed(() => getImageData(imageId.value)),
extent: computed(() => getImageSpatialExtent(imageId.value)),
isLoading: computed(() => getIsImageLoading(imageId.value)),
layers: computed(() => getImageLayers(imageId.value)),
});
</script>

<template>
<slot />
</template>
34 changes: 28 additions & 6 deletions src/components/LayoutGrid.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@
<div v-for="(item, i) in items" :key="i" class="d-flex flex-equal">
<layout-grid v-if="item.type === 'layout'" :layout="item" />
<div v-else class="layout-item">
<component
:is="item.component"
:key="item.id"
:id="item.id"
v-bind="item.props"
/>
<current-image-provider :image-id="selectedImageID">
<component
:is="item.component"
:key="item.id"
:id="item.id"
v-bind="item.props"
/>
</current-image-provider>
</div>
</div>
</div>
Expand All @@ -21,6 +23,9 @@
<script lang="ts">
import { Component, computed, defineComponent, PropType, toRefs } from 'vue';
import { storeToRefs } from 'pinia';
import { useDatasetStore } from '@/src/store/datasets';
import { useDICOMStore } from '@/src/store/datasets-dicom';
import CurrentImageProvider from '@/src/components/CurrentImageProvider.vue';
import VtkTwoView from './VtkTwoView.vue';
import VtkThreeView from './VtkThreeView.vue';
import { Layout, LayoutDirection } from '../types/layout';
Expand All @@ -40,11 +45,27 @@ export default defineComponent({
required: true,
},
},
components: {
CurrentImageProvider,
},
setup(props) {
const { layout } = toRefs(props);
const viewStore = useViewStore();
const { viewSpecs } = storeToRefs(viewStore);
const selectedImageID = computed(() => {
const { primarySelection } = useDatasetStore();
const { volumeToImageID } = useDICOMStore();
if (primarySelection?.type === 'image') {
return primarySelection.dataID;
}
if (primarySelection?.type === 'dicom') {
return volumeToImageID[primarySelection.volumeKey] || null;
}
return null;
});
const flexFlow = computed(() => {
return layout.value.direction === LayoutDirection.H
? 'flex-column'
Expand Down Expand Up @@ -73,6 +94,7 @@ export default defineComponent({
return {
items,
flexFlow,
selectedImageID,
};
},
});
Expand Down
109 changes: 53 additions & 56 deletions src/composables/useCurrentImage.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,33 @@
import { computed } from 'vue';
import { useDatasetStore } from '../store/datasets';
import { useDICOMStore } from '../store/datasets-dicom';
import { InjectionKey, Ref, inject } from 'vue';
import { Maybe } from '@/src/types';
import vtkImageData from '@kitware/vtk.js/Common/DataModel/ImageData';
import { ImageMetadata } from '@/src/types/image';
import { Vector2 } from '@kitware/vtk.js/types';
import { defaultImageMetadata, useImageStore } from '../store/datasets-images';
import { useLayersStore } from '../store/datasets-layers';
import { Layer, useLayersStore } from '../store/datasets-layers';
import { createLPSBounds, getAxisBounds } from '../utils/lps';

type SpatialExtent = {
Sagittal: Vector2;
Coronal: Vector2;
Axial: Vector2;
};

export interface CurrentImageContext {
id: Ref<Maybe<string>>;
imageData: Ref<Maybe<vtkImageData>>;
metadata: Ref<ImageMetadata>;
extent: Ref<SpatialExtent>;
isLoading: Ref<boolean>;
layers: Ref<Layer[]>;
}

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 +45,38 @@ 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;

if (primarySelection?.type === 'image') {
return primarySelection.dataID;
}
if (primarySelection?.type === 'dicom') {
return volumeToImageID[primarySelection.volumeKey] || null;
}
return null;
});

const currentImageMetadata = computed(() => {
const { metadata } = imageStore;
const imageID = currentImageID.value;

if (imageID) {
return metadata[imageID];
}
return defaultImageMetadata();
});
export function getImageMetadata(imageID: Maybe<string>) {
const { metadata } = useImageStore();
return imageID ? metadata[imageID] : defaultImageMetadata();
}

const currentImageData = computed(() => {
if (currentImageID.value)
// assumed to be only images for now
return imageStore.dataIndex[currentImageID.value];
return undefined;
});
export function getImageData(imageID: Maybe<string>) {
const { dataIndex } = useImageStore();
return imageID ? dataIndex[imageID] : null;
}

const currentImageExtent = computed(() =>
getImageSpatialExtent(currentImageID.value)
);
export function getIsImageLoading(imageID: Maybe<string>) {
// TODO imageID -> loading status
return !imageID;
}

const isImageLoading = computed(() => {
return !!dataStore.primarySelection && !dataStore.primaryDataset;
});
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 currentLayers = computed(() =>
layersStore
.getLayers(dataStore.primarySelection)
.filter(({ id }) => id in layersStore.layerImages)
);
export function useCurrentImage() {
const context = inject(CurrentImageInjectionKey);
if (!context) throw new Error('useCurrentImage: no CurrentImageContext!');

return {
currentImageData,
currentImageID,
currentImageMetadata,
currentImageExtent,
isImageLoading,
currentLayers,
currentImageData: context.imageData,
currentImageID: context.id,
currentImageMetadata: context.metadata,
currentImageExtent: context.extent,
isImageLoading: context.isLoading,
currentLayers: context.layers,
};
}

0 comments on commit f348023

Please sign in to comment.